home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / ISSUE14 / SYSTEM / HELPDECO.C < prev    next >
Encoding:
Text File  |  1996-05-22  |  236.9 KB  |  6,550 lines

  1. // HELPDECO - Utility-Programm zum Zerlegen von Windows Hilfedateien
  2. // HELPDECO - utility program to dissect Windows help files
  3. //
  4. // HELPDECO zerlegt eine HLP-Datei von Windows 3.0, 3.1, 3.11 und '95 und
  5. // viele MVB-Dateien des Multimedia-Viewers in alle für den jeweiligen
  6. // Hilfecompiler HC30, HC31, HCP und HCW bzw. Multimediacompiler WMVC12
  7. // oder MVC zum erneuten Zusammenbau erforderlichen Dateien. Dazu gehören:
  8. // HPJ - die Projektdatei, als Parameter für den Hilfecompiler anzugeben
  9. // MVP - die Multimediaprojektdatei, als Parameter für den MM-Compiler
  10. // RTF - die Textdatei mit dem gesamten Hilfetext und allen Fußnoten
  11. // PH  - die Phrasen-Datei (wie sie auch vom Hilfecompiler erzeugt wird)
  12. // ICO - ein eventuell der Hilfedatei zugeordnetes Icon
  13. // BMP/WMF/SHG/MRB - alle Bilder in Dateien mit passendem Format
  14. // Baggage - alle als Baggage in der Hilfedatei enthaltenen Dateien
  15. //
  16. // HELPDECO dissects Windows 3.0, 3.1, 3.11 und '95 HLP files and many
  17. // multi media viewer MVB files into all files required for a rebuild
  18. // using HC30, HC31, HCP, and HCW or multi media compilers WMVC12 and MVC
  19. // HPJ - help project file, use as parameter when calling help compiler
  20. // MVP - multi media project file, parameter for multi media compiler
  21. // RTF - text file containing whole content of help file and all footnotes
  22. // PH  - phrases file (same as produced by help compiler)
  23. // ICO - icon of help file if supplied
  24. // BMP/WMF/SHG/MRB - embedded pictures in appropriate format
  25. // Baggage - all baggage files contained in help file
  26. //
  27. // HELPDECO wird von der MS-DOS Kommandozeile aus mit dem Namen der zu
  28. // bearbeitenden Datei, eventuell dem Namen einer internen Datei und
  29. // eventuellen Optionen aufgerufen:
  30. // Call HELPDECO from MS-DOS command line. Supply name of help file
  31. // to use, optional name of internal file, and options if appropriate.
  32. //
  33. // HELPDECO
  34. // Zeigt Benutzungshinweise
  35. // Displays usage
  36. //
  37. // HELPDECO helpfilename
  38. // Zerlegt die Hilfedatei in alle zum erneuten Zusammenbau benötigten
  39. // Dateien. Diese Dateien werden im aktuellen (möglichst leeren)
  40. // Verzeichnis abgelegt. Existierende Dateien werden ohne Rückfrage
  41. // überschrieben wenn die Option /y angegeben wird.
  42. // Decompiles help file into all sources needed for rebuild. All files
  43. // are created in current directory (should be empty). Existing files
  44. // will be overwritten without asking if option /y was specified.
  45. //
  46. // Options: /m kann verwendet werden, um das Durchsuchen von macros
  47. //             nach Topicnamen zu verhindern, wenn dabei Probleme
  48. //             auftreten. Hilfecompiler wird Warnung 4131 melden.
  49. //             May be used to stop parsing macros for topic names.
  50. //             Help compiler will emit Warning 4131.
  51. //          /b kann verwendet werden, um das Auflösen von Browse-
  52. //             Sequenzen zu verhindern, wenn dabei Probleme auftreten.
  53. //             Hilfequelltextdatei enthält dann keine +-Fußnoten.
  54. //             May be used to stop resolving browse sequences. Help
  55. //             source file than contains no + footnotes.
  56. //          /w Erlaubt die Anzeige von Warnungen, die die Meldung 'HELPDECO
  57. //             had problems with' erklären.
  58. //             Enables display of warnings explaining message 'HELPDECO
  59. //             had problems with'.
  60. //
  61. // HELPDECO helpfilename /r
  62. // Erzeugt aus der Hilfedatei eine RTF-Datei, die von WinWord geladen
  63. // dasselbe Aussehen hat wie die von WinHelp angezeigten Hilfeseiten.
  64. // Damit kann eine Hilfedatei komplett gedruckt oder weiterverarbeitet
  65. // werden.
  66. // Converts help file into RTF file of same appearance if loaded into
  67. // WinWord as if displayed by WinHelp. To print or work with complete
  68. // content.
  69. //
  70. // HELPDECO helpfilename /c
  71. // Erzeugt aus der Hilfedatei eine *.CNT-Datei für WinHlp32, die alle
  72. // Kapitel mit Überschriften in der Reihenfolge enthält, in der sie in
  73. // der Hilfedatei auftreten. Die Datei muß dann mit HCW 4.00 oder einem
  74. // Texteditor in eine hierarchische Struktur überarbeitet werden.
  75. // Generates a *.CNT file used by WinHlp32, containing all chapters that
  76. // have titles assigned in the order they appear in the helpfile. This
  77. // file should then be edited using HCW 4.00 or any text editor into a
  78. // hierarchical order.
  79. //
  80. // HELPDECO helpfilename /p
  81. // Prüft Referenzen auf externe Hilfedateien.
  82. // Checks references to external help files.
  83. //
  84. // HELPDECO helpfilename /d
  85. // Zeigt das interne Inhaltsverzeichnis der Hilfedatei
  86. // Displays internal directory of help file
  87. //
  88. // HELPDECO helpfilename /x
  89. // Zeigt das interne Inhaltsverzeichnis als HexDump
  90. // Displays hex dump of internal directory
  91. //
  92. // HELPDECO helpfilename "internalfilename"
  93. // Zeigt die genannte interne Datei in einem passenden Format an, soweit
  94. // die interne Datei anzeigbar ist, sonst als HexDump
  95. // Displays internal file in appropriate format if known, else hex dump
  96. //
  97. // HELPDECO helpfilename "internalfilename" /x
  98. // Zeigt die genannte interne Datei als HexDump
  99. // Displays hex dump of internal file
  100. //
  101. // HELPDECO helpfilename /t
  102. // Zeigt farbcodierten HexDump der |TOPIC-Datei
  103. // Displays color coded hex dump of |TOPIC file
  104. //
  105. // HELPDECO wurde erstellt von / was written by
  106. // Manfred Winterhoff, Geschw.-Scholl-Ring 17, 38444 Wolfsburg, Germany
  107. // CIS 100326,2776
  108. //
  109. // Informieren Sie mich, wenn Sie HELPDECO modifizieren oder erweitern um
  110. // mehr Features und größere Hilfedateien zu bearbeiten.
  111. // Please give me a note if you modify HELPDECO to handle more formats or
  112. // bigger help files.
  113. //
  114. // HELPDECO basiert auf HELPDUMP von Pete Davis veröffentlicht in:
  115. // HELPDECO is based upon HELPDUMP from Pete Davis published in:
  116. // The Windows Help File Format, Dr. Dobbs Journal, Sep/Oct 1993
  117. //
  118. // Die neueste Version von HELPDECO befindet sich stets in:
  119. // The newest public version of HELPDECO is always available at:
  120. // CompuServe Dr. Dobbs Journal DDJFOR Undocumented Corner HELPDCxx.ZIP
  121. //
  122. // HELPDECO ist public domain Software. Der Einsatz erfolgt auf eigene
  123. // Gefahr. Kein Programmteil darf kommerziell verwendet werden. Für das
  124. // Kopieren dürfen keine Gebühren verlangt werden (Sharewarehandel
  125. // Finger weg). Immer auch die Quelltexte weitergeben, da es für einige
  126. // Hilfedateien erforderlich sein kann, das Programm zu verändern.
  127. // HELPDECO is donated to the public domain. Use at your own risk. No
  128. // part of the program may be used commercially. No fees may be charged
  129. // on distributing the program (shareware distributors keep off).
  130. // Always distribute with source as it may be neccessary to modify the
  131. // program to handle certain help files.
  132. //
  133. // Version 1.7
  134. // removed unneccessary output statement
  135. //
  136. // Version 1.6 can now check references to external help files plus:
  137. // duplicate macro names preceeding picture hotspot info skipped
  138. // does not write Win95 commands to multi-media help project files
  139. // changed unhash to circumvent Microsoft-C++ float rounding error
  140. // handles keywords defined inside topic text
  141. //
  142. // Version 1.5
  143. // fixed static on buffer of TopicName function (affected HC30 files)
  144. //
  145. // Version 1.4 fixes some bugs reported by different users:
  146. // buffer overflow in expanding LZ77&RunLen (byPacked 3) images fixed
  147. // embedded images {bmxwd} larger than 32k supported
  148. // extract topic names from jump into external file if no file specified
  149. // handles more phrases on HCW 4.00 (Win95) help files
  150. // Windows 3.1 (HC31) |Phrases always Zeck compressed
  151. // LinkData2 buffer enlarged 1 byte to store trailing NUL character
  152. //
  153. // Version 1.3
  154. // parses examples of {bmc} etc. statements contained in help text correctly
  155. // can now generate a *.CNT content file for Windows 95 / WinHlp32
  156. // Microsoft C: ctype macros (isalnum/isprint) don't work with signed char
  157. //
  158. // Version 1.2 fixes some severe bugs introduced in version 1.1 and:
  159. // tells you which help compiler to use
  160. // collects multiple keyword footnotes into single lines
  161. // handles \r\n in COPYRIGHT
  162. // converts SPC-macro (but only in [CONFIG] section)
  163. // does not generate duplicate MAP-statements if possible
  164. // {button} and {mci,mci_left,mci_right} commands supported
  165. // [BITMAP]-section in HCW 4.00 help files irritated transparent bitmaps
  166. //
  167. // Version 1.1 now supports more features of Win95/HCW 4.00/WinHlp32:
  168. // Supports LCID, CHARSET, AUTO-SIZE HEIGHT, CNT, INDEX_SEPARATORS
  169. // Additional Win95 Macros (to extract original topic names)
  170. // [CONFIG:n] of Win95 supported (internal file |CFn)
  171. // Secondary windows with > footnote supported (internal file |VIOLA)
  172. // Transparent bitmaps supported (bmct,bmlt,bmrt)
  173. // Expanded internal limits as HCW 4.00 allows larger items
  174. // Now does RunLen compressed device dependend bitmaps
  175. // Bugs in handling of metafiles removed
  176. // Bug in placement of pack(1) removed
  177. // Parsing of macros changed (is it really better now ?)
  178. //
  179. // HELPDECO wurde mit über 500 Hilfedateien getestet. Aber einige gehen nicht
  180. // HELPDECO was tested with more than 500 help files. But some don't work
  181. // PRINTMAN.HLP    50.743 03/10/92  3:10 (Hilfe zum Druck-Manager) corrupt
  182. // BACKSDK.MVB  9.367.750 08/09/95 13:30 8b/8c unknown
  183. // bei anderen treten beim Dekompilieren Probleme auf
  184. // others exhibit problems during decompilation
  185. // ISQLW.HLP      693.763 11/16/94 12:00 (Watcom SQL)
  186. // WSQLODBC.HLP    14.995 11/16/94 12:00 (Watcom SQL ODBC)
  187. // FONTEDIT.HLP    16.124 03/10/95 12:00 or 03/23/92  3:10
  188. // UNIDRV.HLP      45.107 31/12/93  3:11
  189. // WIN31WH.HLP  3.386.753 11/17/94  4:50 (Windows 3.1 SDK)
  190. // und einige Hilfedateien sind einfach zu groß wie
  191. // and certain help files are simply too big, like
  192. // DELPHI.HLP   4.765.203 02/17/95  8:00 (DELPHI Hilfe)
  193. // WIN32.HLP   21.992.070 07/11/95  9:50 (Win32 SDK)
  194. //
  195. #include <time.h>
  196. #include <malloc.h>
  197. #include <stdio.h>
  198. #include <stdarg.h>
  199. #include <stdlib.h>
  200. #include <string.h>
  201. #include <conio.h>
  202. #include <ctype.h>
  203. #include <limits.h>
  204. #include <math.h>
  205.  
  206. // compile in large memory model if large help files should be handled
  207. // neccessary compiler options using Microsoft C/C++:
  208. //   cl -AL -Os -J -F 1000 helpdeco.c
  209. // neccessary compiler options using Borland C/C++:
  210. //   bcc -ml -K helpdeco.c
  211. // You may try to compile with 32-bit compiler to handle even larger
  212. // help files, but I don't have one, so it may need changes.
  213. // byte align !
  214. #ifdef __TURBOC__
  215. typedef struct { char a,b,c; } align;
  216. #if sizeof(align)!=3
  217. #error Compile bytealigned
  218. #endif
  219. #else
  220. #pragma pack(1)
  221. #endif
  222.  
  223. typedef struct HELPHEADER    // structure at beginning of help file
  224. {
  225.     unsigned short Magic;    // 0x5F3F
  226.     unsigned short Version;  // 0x0003
  227.     long DirectoryStart;     // offset of FILEHEADER of internal direcory
  228.     long Negative1;          // -1L
  229.     long EntireFileSize;     // size of entire help file in bytes
  230. }
  231. HELPHEADER;
  232.  
  233. typedef struct FILEHEADER    // structure at FileOffset of each internal file
  234. {
  235.     long FileSizePlus9;      // size of internal file including FILEHEADER
  236.     long FileSize;           // size of internal file in bytes
  237.     char FileFlags;          // normally 0
  238. }
  239. FILEHEADER;
  240.  
  241. typedef struct BTREEHEADER   // structure after FILEHEADER of each Btree
  242. {
  243.     unsigned short Magic;    // 0x293B
  244.     unsigned short Flags;    // bit 0x0002 always 1, bit 0x0400 1 if direcory
  245.     unsigned short PageSize; // 0x0400=1k if directory, 0x0800=2k else
  246.     char Unknown[16];
  247.     short MustBeZero;        // 0
  248.     short PageSplits;        // number of page splits Btree has suffered
  249.     short RootPage;          // page number of Btree root page
  250.     short MustBeNegOne;      // 0xFFFF
  251.     short TotalPages;        // number of Btree pages
  252.     short NLevels;           // number of levels of Btree
  253.     long TotalBtreeEntries;  // number of entries in Btree
  254. }
  255. BTREEHEADER;
  256.  
  257. typedef struct BTREEINDEXHEADER // structure at beginning of every index-page
  258. {
  259.     unsigned short Unknown;  // sorry, no ID to identify an index-page
  260.     short NEntries;          // number of entries in this index-page
  261.     short PreviousPage;      // page number of previous page
  262. }
  263. BTREEINDEXHEADER;
  264.  
  265. typedef struct BTREENODEHEADER // structure at beginning of every leaf-page
  266. {
  267.     unsigned short Unknown;  // Sorry, no ID to identify a leaf-oage
  268.     short NEntries;          // number of entires in this leaf-page
  269.     short PreviousPage;      // page number of preceeding leaf-page or -1
  270.     short NextPage;          // page number of next leaf-page or -1
  271. }
  272. BTREENODEHEADER;
  273.  
  274. typedef struct SYSTEMHEADER  // structure at beginning of |SYSTEM file
  275. {
  276.     unsigned char Magic;     // 0x6C
  277.     unsigned char Version;   // version # always 3
  278.     unsigned char Revision;  // revision code
  279.     unsigned char Always0;
  280.     unsigned short Always1;
  281.     time_t GenDate;          // date/time that the help file was generated or 0
  282.     unsigned short Flags;    // tells you how the help file is compressed
  283. }
  284. SYSTEMHEADER;
  285.  
  286. typedef struct SYSTEMREC     // structure following version 3.1 SYSTEMHEADER
  287. {
  288.     unsigned short RecordType; // type of data in record
  289.     unsigned short DataSize;   // size of data
  290. }
  291. SYSTEMREC;
  292.  
  293. typedef struct SECWINDOW     // structure of data of following RecordType 6
  294. {
  295.     unsigned short Flags;    // flags (See Below)
  296.     char Type[10];           // type of window
  297.     char Name[9];            // window name
  298.     char Caption[51];        // caption for window
  299.     short X;                 // x coordinate of windows (0..1000)
  300.     short Y;                 // y coordinate of window (0..1000)
  301.     short Width;             // width of windows (0..1000)
  302.     short Height;            // height of windows (0..1000)
  303.     short Maximize;          // maximize flag and window styles
  304.     unsigned char Rgb[3];    // color of scrollable region
  305.     unsigned char Unknown1;
  306.     unsigned char RgbNsr[3]; // color of non-scrollable region
  307.     unsigned char Unknown2;
  308. }
  309. SECWINDOW;
  310.  
  311. typedef struct               // structure of data following RecordType 14
  312. {
  313.     char btreename[10];
  314.     char mapname[10];
  315.     char dataname[10];
  316.     char title[80];
  317. }
  318. KEYINDEX;
  319.  
  320. #define WSYSFLAG_TYPE           0x0001  // Type is valid
  321. #define WSYSFLAG_NAME           0x0002  // Name is valid
  322. #define WSYSFLAG_CAPTION        0x0004  // Caption is valid
  323. #define WSYSFLAG_X              0x0008  // X is valid
  324. #define WSYSFLAG_Y              0x0010  // Y is valid
  325. #define WSYSFLAG_WIDTH          0x0020  // Width is valid
  326. #define WSYSFLAG_HEIGHT         0x0040  // Height is valid
  327. #define WSYSFLAG_MAXIMIZE       0x0080  // Maximize is valid
  328. #define WSYSFLAG_RGB            0x0100  // Rgb is valid
  329. #define WSYSFLAG_RGBNSR         0x0200  // RgbNsr is valid
  330. #define WSYSFLAG_TOP            0x0400  // On top was set in HPJ file
  331. #define WSYSFLAG_AUTOSIZEHEIGHT 0x0800  // Auto-Size Height
  332.  
  333. typedef struct PHRINDEXHDR   // structure of beginning of |PhrIndex file
  334. {
  335.     long always4A01;              // sometimes 0x0001
  336.     long entries;                 // number of phrases
  337.     long compressedsize;          // size of PhrIndex file
  338.     long phrimagesize;            // size of decompressed PhrImage file
  339.     long phrimagecompressedsize;  // size of PhrImage file
  340.     long always0;
  341.     unsigned short bits:4;
  342.     unsigned short unknown:12;
  343.     unsigned short always4A00;    // sometimes 0x4A01, 0x4A02
  344. }
  345. PHRINDEXHDR;
  346.  
  347. typedef struct FONTHEADER    // structure of beginning of |FONT file
  348. {
  349.     unsigned short NumFacenames;       // number of face names
  350.     unsigned short NumDescriptors;     // number of font descriptors
  351.     unsigned short FacenamesOffset;    // offset of face name array
  352.     unsigned short DescriptorsOffset;  // offset of descriptors array
  353.     unsigned short NumFormats;         // only if FacenamesOffset >= 12
  354.     unsigned short FormatsOffset;      // offset of formats array
  355. }
  356. FONTHEADER;
  357.  
  358. typedef struct FONTDESCRIPTOR // structure located at DescriptorsOffset
  359. {
  360.     unsigned char Attributes; // Font Attributes See values below
  361.     unsigned char HalfPoints; // PointSize * 2
  362.     unsigned char FontFamily; // Font Family. See values below
  363.     unsigned short FontName;  // Number of newfont in Font List
  364.     unsigned char FGRGB[3];   // RGB values of foreground
  365.     unsigned char BGRGB[3];   // unused background RGB Values
  366. }
  367. FONTDESCRIPTOR;
  368.  
  369. typedef struct NEWFONT        // structure located at DescriptoresOffset
  370. {
  371.     unsigned char unknown1;
  372.     short FontName;
  373.     unsigned char FGRGB[3];
  374.     unsigned char BGRGB[3];
  375.     unsigned char unknown5;
  376.     unsigned char unknown6;
  377.     unsigned char unknown7;
  378.     unsigned char unknown8;
  379.     unsigned char unknown9;
  380.     long Height;
  381.     unsigned char mostlyzero[12];
  382.     short Weight;
  383.     unsigned char unknown10;
  384.     unsigned char unknown11;
  385.     unsigned char Italic;
  386.     unsigned char Underline;
  387.     unsigned char StrikeOut;
  388.     unsigned char DoubleUnderline;
  389.     unsigned char SmallCaps;
  390.     unsigned char unknown17;
  391.     unsigned char unknown18;
  392.     unsigned char PitchAndFamily;
  393. }
  394. NEWFONT;
  395. // Font Attributes
  396. #define FONT_NORM 0x00 // Normal
  397. #define FONT_BOLD 0x01 // Bold
  398. #define FONT_ITAL 0x02 // Italics
  399. #define FONT_UNDR 0x04 // Underline
  400. #define FONT_STRK 0x08 // Strike Through
  401. #define FONT_DBUN 0x10 // Dbl Underline
  402. #define FONT_SMCP 0x20 // Small Caps
  403. // Font Families
  404. #define FAM_MODERN 0x01
  405. #define FAM_ROMAN  0x02
  406. #define FAM_SWISS  0x03
  407. #define FAM_TECH   0x03
  408. #define FAM_NIL    0x03
  409. #define FAM_SCRIPT 0x04
  410. #define FAM_DECOR  0x05
  411.  
  412. typedef struct KWMAPREC       // structure of |xWMAP leaf-page entries
  413. {
  414.     long FirstRec;            // index number of first keyword on leaf page
  415.     unsigned short PageNum;   // page number that keywords are associated with
  416. }
  417. KWMAPREC;
  418.  
  419. typedef struct TOPICBLOCKHEADER // structure every TopicBlockSize of |TOPIC
  420. {
  421.     long LastTopicLink;       // offset of last topic link in previous block
  422.     long TopicData;           // offset of topic data start
  423.     long LastTopicHeader;     // offset of last topic header in previous block
  424. }
  425. TOPICBLOCKHEADER;
  426.  
  427. typedef struct TOPICLINK      // structure pointed to be TopicData
  428. {
  429.     long BlockSize;           // size of this link + LinkData1 + LinkData2
  430.     long DataLen2;            // length of decompressed LinkData2
  431.     long PrevBlock;           // relative to first byte of |TOPIC
  432.     long NextBlock;           // relative to first byte of |TOPIC
  433.     long DataLen1;            // includes size of TOPICLINK
  434.     unsigned char RecordType; // See below
  435. }
  436. TOPICLINK;
  437. // Known RecordTypes for TOPICLINK
  438. #define TL_DISPLAY30 0x01     // version 3.0 displayable information
  439. #define TL_TOPICHDR  0x02     // topic header information
  440. #define TL_DISPLAY   0x20     // version 3.1 displayable information
  441. #define TL_TABLE     0x23     // version 3.1 table
  442.  
  443. typedef struct TOPICHEADER    // structure of LinkData1 of RecordType 2
  444. {
  445.     long BlockSize; // size of topic, including internal topic links
  446.     long BrowseBck; // topic offset for prev topic in browse sequence
  447.     long BrowseFor; // topic offset for next topic in browse sequence
  448.     long TopicNum;  // topic Number
  449.     long NonScroll; // start of non-scrolling region (topic offset) or -1
  450.     long Scroll;    // start of scrolling region (topic offset)
  451.     long NextTopic; // start of next type 2 record
  452. }
  453. TOPICHEADER;
  454.  
  455. typedef struct TOPICHEADER30  // structure of LinkData1 of RecordType 2
  456. {
  457.     long BlockSize;
  458.     short PrevTopicNum;
  459.     short unused1;
  460.     short NextTopicNum;
  461.     short unused2;
  462. }
  463. TOPICHEADER30;
  464.  
  465. typedef struct CTXOMAPREC     // structure of |CTXOMAP file entries
  466. {
  467.     long MapID;
  468.     long TopicOffset;
  469. }
  470. CTXOMAPREC;
  471.  
  472. typedef struct CONTEXTREC     // structure of |CONTEXT index-page entry
  473. {
  474.     long HashValue;           // Hash value of Topic Name
  475.     long TopicOffset;         // Topic offset
  476. }
  477. CONTEXTREC;
  478.  
  479. typedef struct tagBITMAPFILEHEADER
  480. {
  481.     unsigned short bfType;
  482.     unsigned long bfSize;
  483.     unsigned short bfReserved1;
  484.     unsigned short bfReserved2;
  485.     unsigned long bfOffBits;
  486. }
  487. BITMAPFILEHEADER;
  488.  
  489. typedef struct tagBITMAPINFOHEADER
  490. {
  491.     unsigned long biSize;
  492.     long biWidth;
  493.     long biHeight;
  494.     unsigned short biPlanes;
  495.     unsigned short biBitCount;
  496.     unsigned long biCompression;
  497.     unsigned long biSizeImage;
  498.     long biXPelsPerMeter;
  499.     long biYPelsPerMeter;
  500.     unsigned long biClrUsed;
  501.     unsigned long biClrImportant;
  502. }
  503. BITMAPINFOHEADER;
  504.  
  505. typedef struct tagRECT
  506. {
  507.     short left;
  508.     short top;
  509.     short right;
  510.     short bottom;
  511. }
  512. RECT;
  513.  
  514. typedef struct tagAPMFILEHEADER
  515. {
  516.     unsigned long dwKey;
  517.     unsigned short hMF;
  518.     RECT rcBBox;
  519.     unsigned short wInch;
  520.     unsigned long dwReserved;
  521.     unsigned short wChecksum;
  522. }
  523. APMFILEHEADER;
  524.  
  525. typedef struct
  526. {
  527.     unsigned char id0,id1,id2;
  528.     unsigned short x,y,w,h;
  529.     unsigned long hash;
  530. }
  531. HOTSPOT;
  532.  
  533. typedef enum {FALSE,TRUE} BOOL;
  534.  
  535. typedef struct BUFFER         // structure used as buf of GetFirstPage
  536. {
  537.     long FirstLeaf;
  538.     unsigned short PageSize;
  539.     short NextPage;
  540. }
  541. BUFFER;
  542.  
  543. typedef struct browse // internal use. max. 3640 / 64k
  544. {
  545.     long StartTopic;
  546.     long NextTopic;
  547.     long PrevTopic;
  548.     short BrowseNum;
  549.     short Start;
  550.     short Count;
  551. }
  552. BROWSE;
  553.  
  554. typedef struct start // internal use. max. 8191 / 64k
  555. {
  556.     long StartTopic;
  557.     short BrowseNum;
  558.     short Start;
  559. }
  560. START;
  561.  
  562. typedef struct hashrec // internal use. max. 8191 / 64k
  563. {
  564.     char *name;
  565.     long hash;
  566. }
  567. HASHREC;
  568.  
  569. #define MAGIC 0x5774
  570.  
  571. typedef struct
  572. {
  573.     int magic;
  574.     char *ptr;
  575.     char *end;
  576. }
  577. MFILE;
  578.  
  579. char drive[_MAX_DRIVE];
  580. char dir[_MAX_DIR];
  581. char name[_MAX_FNAME];
  582. char ext[_MAX_EXT];
  583. HELPHEADER HelpHeader;
  584. HASHREC *hashrec;
  585. BROWSE *browse;
  586. int scaling=10;
  587. int browses;
  588. int browsenums;
  589. START *start;
  590. int starts;
  591. int hashrecs=0;
  592. BOOL before31;
  593. BOOL warn=FALSE;
  594. BOOL warnings=FALSE;
  595. int missing=0;
  596. BOOL lzcompressed;
  597. long *Topic;
  598. int Topics;                         // max. 16348 Topics
  599. CONTEXTREC *ContextRec;
  600. BOOL overwrite=FALSE;
  601. BOOL extractmacros=TRUE;
  602. BOOL resolvebrowse=TRUE;
  603. BOOL checkexternal=FALSE;
  604. char helpcomp[10];
  605. char HelpFileTitle[51];
  606. int ContextRecs;                    // max. 8191 Context Records
  607. long TopicFileStart=0L;
  608. SYSTEMHEADER SysHeader;             // Global System Header Record
  609. unsigned short *Offsets;
  610. unsigned int PhraseCount;
  611. int TopicUse;
  612. int TopicBlockSize; // 2k or 4k
  613. int DecompressSize; // 4k or 16k
  614. unsigned char buffer[0x4000];
  615. char index_separators[40]=",;";
  616. int extensions=0;
  617. char *bmpext[]={"???","BMP","SHG","MRB","WMF","BMP","SHG"};
  618. BOOL mvp;
  619. char *extension;
  620. int fontnames;
  621. char **fontname;
  622. long TopicPos;
  623. long TopicOffset;
  624. long TopicSize;
  625. int fonts;
  626. char *NewPhrases;
  627. FONTDESCRIPTOR *font;
  628. NEWFONT *newfont;
  629. int newfonts;
  630. BOOL dontCount;
  631. char **secondarywindownames; // Array NULL terminated
  632. BOOL NotInAnyTopic;
  633. BOOL lists['z'-'A'+1];
  634. BOOL keyindex['z'-'A'+1];
  635. unsigned char table[256];
  636.  
  637. void error(char *format,...)
  638. {
  639.     va_list arg;
  640.  
  641.     va_start(arg,format);
  642.     vfprintf(stderr,format,arg);
  643.     va_end(arg);
  644.     fprintf(stderr,"Press CR to continue at your own risk, any other key to exit.\n");
  645.     if(getch()!='\r') exit(1);
  646. }
  647.  
  648. long myFTell(MFILE *f)
  649. {
  650.     if(f->magic!=MAGIC) return ftell((FILE *)f);
  651.     return (long)f->ptr;
  652. }
  653.  
  654. void myFSeek(MFILE *f,long offset)
  655. {
  656.     if(f->magic!=MAGIC)
  657.     {
  658.         fseek((FILE *)f,offset,SEEK_SET);
  659.     }
  660.     else
  661.     {
  662.         f->ptr=(char *)offset;
  663.     }
  664. }
  665.  
  666. void *myMalloc(long bytes) // save malloc function
  667. {
  668.     void *ptr;
  669.  
  670.     if(bytes<1L||((size_t)bytes!=bytes)||(ptr=malloc((size_t)bytes))==NULL)
  671.     {
  672.         fprintf(stderr,"Allocation of %ld bytes failed. File too big.\n",bytes);
  673.         exit(1);
  674.     }
  675.     return ptr;
  676. }
  677.  
  678. void *myReAlloc(void *ptr,long bytes) // save realloc function
  679. {
  680.     if(!ptr) return myMalloc(bytes);
  681.     if(bytes<1L||bytes!=(size_t)bytes||(ptr=realloc(ptr,(size_t)bytes))==NULL)
  682.     {
  683.         fprintf(stderr,"Reallocation to %ld bytes failed. File too big.\n",bytes);
  684.         exit(1);
  685.     }
  686.     return ptr;
  687. }
  688.  
  689. char *myStrDup(char *ptr) // save strdup function
  690. {
  691.     size_t len;
  692.     char *dup;
  693.  
  694.     if(!ptr) return NULL;
  695.     len=strlen(ptr);
  696.     dup=myMalloc(len+1L);
  697.     strcpy(dup,ptr);
  698.     return dup;
  699. }
  700.  
  701. long hash(char *name) // convert 3.1 topic name to hash value
  702. {
  703.     long hash;
  704.     unsigned char *ptr;
  705.  
  706.     if(*name=='\0') return 1L;
  707.     for(hash=0L,ptr=(unsigned char *)name;*ptr;ptr++)
  708.     {
  709.         if(table[*ptr]==0)
  710.         {
  711.             fprintf(stderr,"Illegal char %c in Topic Name %s\n",*ptr,name);
  712.             warnings=TRUE;
  713.             return 0L;
  714.         }
  715.         hash=(hash*0x2BU)+table[*ptr];
  716.     }
  717.     return hash;
  718. }
  719.  
  720. char *unhash(unsigned long hash) // deliver 3.1 topic name that fits hash value
  721. {
  722.     static unsigned char untable[]={0,'1','2','3','4','5','6','7','8','9','0',0,'.','_',0,0,0,'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
  723.     unsigned int i,n,f,t;
  724.     long double x;
  725.     static char buffer[15];
  726.  
  727.     for(i=0;i<hashrecs;i++) if(hashrec[i].hash==hash) return hashrec[i].name;
  728.     for(t=0;t<65535U;t++)
  729.     {
  730.         x=hash+256.0*256.0*256.0*256.0*t;
  731.         buffer[i=14]='\0';
  732.         while(1)
  733.         {
  734.             n=(int)fmodl(x,43.0);
  735.             f=untable[n];
  736.             if(!f) break;
  737.             x-=n;
  738.             buffer[--i]=f;
  739.             x/=43.0;
  740.             if(x<1.0) return buffer+i;
  741.         }
  742.     }
  743.     // should never happen
  744.     error("Can not find a matching string for hash value %08lx\n",hash);
  745.     sprintf(buffer,"HASH%08lx",hash);
  746.     return buffer;
  747. }
  748.  
  749. size_t myFRead(void *ptr,long bytes,FILE *f) // save fread function
  750. {
  751.     size_t result;
  752.  
  753.     if(bytes==0) return 0;
  754.     if(bytes<1||bytes!=(size_t)bytes||(result=fread(ptr,1,(size_t)bytes,f))!=bytes)
  755.     {
  756.         error("myFRead(%ld) at %ld failed\n",bytes,ftell(f));
  757.     }
  758.     return result;
  759. }
  760.  
  761. size_t StringRead(char *ptr,size_t size,FILE *f) // read nul terminated string
  762. {
  763.     size_t i;
  764.     int c;
  765.  
  766.     i=0;
  767.     while((c=getc(f))>0)
  768.     {
  769.         if(i>=size-1)
  770.         {
  771.             fprintf(stderr,"\nKeyword length exceeds decompiler limit.\n");
  772.             exit(1);
  773.         }
  774.         ptr[i++]=c;
  775.     }
  776.     ptr[i]='\0';
  777.     return i;
  778. }
  779.  
  780. void myFClose(FILE *f) // checks if disk is full
  781. {
  782.     if(ferror(f)!=0)
  783.     {
  784.         fprintf(stderr,"\nDisk full. Program aborted.\n");
  785.         exit(2);
  786.     }
  787.     fclose(f);
  788. }
  789.  
  790. FILE *myFOpen(const char *filename,const char *mode) // save fopen function
  791. {
  792.     FILE *f;
  793.     char ch;
  794.  
  795.     if(!overwrite)
  796.     {
  797.         f=fopen(filename,"rb");
  798.         if(f)
  799.         {
  800.             fclose(f);
  801.             fprintf(stderr,"File %s already exists. Overwrite (Y/N/All) ? Y\b",filename);
  802.             do
  803.             {
  804.                 ch=getch();
  805.             }
  806.             while(ch!='a'&&ch!='A'&&ch!='y'&&ch!='Y'&&ch!='n'&&ch!='N'&&ch!='\r'&&ch!='\x1B');
  807.             if(ch=='\r') ch='Y';
  808.             if(ch=='\x1B') ch='N';
  809.             printf("%c\n",ch);
  810.             if(ch=='A'||ch=='a')
  811.             {
  812.                 ch='Y';
  813.                 overwrite=TRUE;
  814.             }
  815.             if(ch=='n'||ch=='N') exit(0);
  816.         }
  817.     }
  818.     f=fopen(filename,mode);
  819.     if(!f)
  820.     {
  821.         error("Can not create '%s'.\n",filename);
  822.     }
  823.     else if(((MFILE *)f)->magic==MAGIC)
  824.     {
  825.         fprintf(stderr,"Error creating %s...\n",filename);
  826.         fclose(f);
  827.         f=NULL;
  828.     }
  829.     else
  830.     {
  831.         fprintf(stderr,"Creating %s...\n",filename);
  832.     }
  833.     return f;
  834. }
  835.  
  836. char *WindowName(long n) // secondary window name from secondary window number
  837. {
  838.     int i;
  839.  
  840.     if(n==255||secondarywindownames==NULL) return "main";
  841.     for(i=0;secondarywindownames[i];i++)
  842.     {
  843.         if(i==n) return secondarywindownames[i];
  844.     }
  845.     error("Secondary window %ld not defined",n);
  846.     return "main";
  847. }
  848.  
  849. void AddTopic(char *TopicName) // adds a known topic name to hash decode list
  850. {
  851.     long x;
  852.     int i;
  853.  
  854.     x=hash(TopicName);
  855.     for(i=0;i<hashrecs;i++)
  856.     {
  857.         if(hashrec[i].hash==x)
  858.         {
  859.             if(stricmp(TopicName,hashrec[i].name)!=0)
  860.             {
  861.                 fprintf(stderr,"Context %s already defined as %s\n",TopicName,hashrec[i].name);
  862.             }
  863.             return;
  864.         }
  865.     }
  866.     hashrec=myReAlloc(hashrec,++hashrecs*sizeof(HASHREC));
  867.     hashrec[i].name=myStrDup(TopicName);
  868.     hashrec[i].hash=x;
  869. }
  870.  
  871. // Why does HELPDECO try to isolate topic names from macro parameters ?
  872. // Shure, HELPDECO can generate a topic name that has the same hash code
  873. // as in WinHelp. So a topic jump into an external file will not fail.
  874. // But if a macro or hotspot uses the original topic name and HELPDECO
  875. // invents a different name delivering the same hash code, the help
  876. // compiler will emit annoying warnings. That's why we collect all
  877. // original names that we can find and generate topic names only if
  878. // no original name has been found.
  879. #if 0
  880. void AddMacro(char *ptr)
  881. {
  882.     int i,n;
  883.     char buffer[128];
  884.     char *param[10];
  885.  
  886.     while(1)
  887.     {
  888.         while(*ptr==' ') ptr++;
  889.         for(i=0;i<sizeof(buffer)&&*ptr!=':'&&*ptr!='\0'&&*ptr!=';'&&*ptr!='(';i++) buffer[i]=*ptr++;
  890.         while(i>0&&buffer[i-1]==' ') i--;
  891.         if(*ptr=='(')
  892.         {
  893.             buffer[i++]=ptr++;
  894.             for(n=0;n<sizeof(param)/sizeof(param[0]);n++)
  895.             {
  896.                 while(*ptr==' ') ptr++;
  897.                 if(*ptr==')') break;
  898.                 if(*ptr=='\"')
  899.                 {
  900.                     param[n]=++ptr;
  901.                     while(*ptr!='\0'&&*ptr!='\"') ptr++;
  902.                     if(*ptr=='\0') return;
  903.                     *ptr++='\0';
  904.                 }
  905.                 else if(*ptr=='`')
  906.                 {
  907.                     param[n]=++ptr;
  908.                     while(*ptr!='\0'&&*ptr!='\'') ptr++;
  909.                     if(*ptr=='\0') return;
  910.                     *ptr++='\0';
  911.                 }
  912.                 else
  913.                 {
  914.                     param[n]=ptr;
  915.                     while(*ptr!='\0'&&*ptr!=','&&*ptr!=')') ptr++;
  916.                     if(*ptr=='\0') return;
  917.                 }
  918.                 while(*ptr==' ') ptr++;
  919.                 if(*ptr==')') break;
  920.                 if(*ptr!=',') return;
  921.                 *ptr++='\0';
  922.             }
  923.             *ptr++='\0';
  924.             while(*ptr==' ') ptr++;
  925.         }
  926.         if(*ptr!=':'&&*ptr!=':') break;
  927.         ptr++;
  928.     }
  929. }
  930. #else
  931. char *AddMacro(char *ptr)
  932. {
  933.     // table listing known macro names and type of parameters
  934.     // m specifies a parameter that can be a macro oder marker-name
  935.     // c specifies a parameter that has to be a context ID
  936.     // f specifies a parameter that has to be a "filename>window"
  937.     // s specifies a parameter that can be ignored
  938.     char *macro[]={"AddAccelerator(ssm","AA(ssm","AppendItem(sssm","AI(sssm","ChangeButtonBinding(sm","CB(ssm","CBB(sm","CE(sm","ChangeItemBinding(sm","CIB(sm","ExtInsertItem(sssm","IfThen(mm","IT(mm","IfThenElse(mmm","IE(mmm","InsertItem(sssms","II(sssms","JumpId(fc","JI(fc","MPrintID(c","Not(m","PopupId(fc","PI(fc","UpdateWindow(fc","UW(fc"};
  939.     char *name;
  940.     char *param;
  941.     int i,n;
  942.  
  943.     while(1)
  944.     {
  945.         while(*ptr==' ') ptr++;
  946.         name=ptr;
  947.         for(n=0;isalnum((unsigned char)*ptr);n++) ptr++;
  948.         while(*ptr==' ') ptr++;
  949.         if(*ptr=='(')
  950.         {
  951.             ptr++;
  952.             for(i=0;i<sizeof(macro)/sizeof(macro[0]);i++)
  953.             {
  954.                 if(n==strcspn(macro[i],"(")&&memicmp(macro[i],name,n)==0) break;
  955.             }
  956.             if(i<sizeof(macro)/sizeof(macro[0]))
  957.             {
  958.                 name=macro[i]+n+1;
  959.             }
  960.             else
  961.             {
  962.                 name=NULL;
  963.             }
  964.             while(1)
  965.             {
  966.                 while(*ptr==' ') ptr++;
  967.                 if(*ptr=='"'||*ptr=='`')
  968.                 {
  969.                     if(*ptr=='\\')
  970.                     {
  971.                         ptr+=2;
  972.                     }
  973.                     else
  974.                     {
  975.                         ptr++;
  976.                     }
  977.                     param=ptr;
  978.                     if(name&&*name=='m')
  979.                     {
  980.                         ptr=AddMacro(ptr);
  981.                         while(*ptr==' ') ptr++;
  982.                     }
  983.                     else
  984.                     {
  985.                         while(*ptr!='\0'&&*ptr!='"'&&*ptr!='\'') ptr++;
  986.                     }
  987.                     if(*ptr!='"'&&*ptr!='\'') break;
  988.                     *ptr='\0';
  989.                     if(name&&*name=='c') AddTopic(param);
  990.                     ptr++;
  991.                 }
  992.                 else
  993.                 {
  994.                     while(isalnum((unsigned char)*ptr)) ptr++;
  995.                 }
  996.                 while(*ptr==' ') ptr++;
  997.                 if(*ptr!=',') break;
  998.                 ptr++;
  999.                 if(name) name++;
  1000.             }
  1001.             while(*ptr==' ') ptr++;
  1002.             if(*ptr!=')') break;
  1003.             ptr++;
  1004.         }
  1005.         while(*ptr==' ') ptr++;
  1006.         if(*ptr!=';'&&*ptr!=':') break;
  1007.         ptr++;
  1008.     }
  1009.     return ptr;
  1010. }
  1011. #endif
  1012. BOOL SearchFile(FILE *HelpFile,char *FileName,long *FileOffset) // locate an internal file
  1013. {
  1014.     FILEHEADER FileHdr;
  1015.     BTREEHEADER WHIFSHeader;
  1016.     BTREENODEHEADER CurrNode;
  1017.     long offset;
  1018.     char TempFile[19];
  1019.     int i,n;
  1020.  
  1021.     fseek(HelpFile,HelpHeader.DirectoryStart,SEEK_SET);
  1022.     myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  1023.     myFRead(&WHIFSHeader,sizeof(WHIFSHeader),HelpFile);
  1024.     offset=ftell(HelpFile);
  1025.     fseek(HelpFile,offset+WHIFSHeader.RootPage*(long)WHIFSHeader.PageSize,SEEK_SET);
  1026.     for(n=1;n<WHIFSHeader.NLevels;n++)
  1027.     {
  1028.         myFRead(&CurrNode,sizeof(BTREEINDEXHEADER),HelpFile);
  1029.         for(i=0;i<CurrNode.NEntries;i++)
  1030.         {
  1031.             StringRead(TempFile,sizeof(TempFile),HelpFile);
  1032.             if(strcmp(FileName,TempFile)<0) break;
  1033.             myFRead(&CurrNode.PreviousPage,sizeof(CurrNode.PreviousPage),HelpFile);
  1034.         }
  1035.         fseek(HelpFile,offset+CurrNode.PreviousPage*(long)WHIFSHeader.PageSize,SEEK_SET);
  1036.     }
  1037.     myFRead(&CurrNode,sizeof(CurrNode),HelpFile);
  1038.     for(i=0;i<CurrNode.NEntries;i++)
  1039.     {
  1040.         StringRead(TempFile,sizeof(TempFile),HelpFile);
  1041.         myFRead(FileOffset,sizeof(long),HelpFile);
  1042.         if(strcmp(TempFile,FileName)==0) return TRUE;
  1043.     }
  1044.     return FALSE;
  1045. }
  1046.  
  1047. short GetFirstPage(FILE *HelpFile,long FileStart,BUFFER *buffer) // walk Btree
  1048. {
  1049.     int CurrLevel;
  1050.     FILEHEADER FileHdr;
  1051.     BTREEHEADER BTreeHdr;
  1052.     BTREENODEHEADER CurrNode;
  1053.  
  1054.     fseek(HelpFile,FileStart,SEEK_SET);
  1055.     myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  1056.     myFRead(&BTreeHdr,sizeof(BTreeHdr),HelpFile);
  1057.     if(!BTreeHdr.TotalBtreeEntries) return 0;
  1058.     buffer->FirstLeaf=ftell(HelpFile);
  1059.     buffer->PageSize=BTreeHdr.PageSize;
  1060.     fseek(HelpFile,buffer->FirstLeaf+BTreeHdr.RootPage*(long)BTreeHdr.PageSize,SEEK_SET);
  1061.     for(CurrLevel=1;CurrLevel<BTreeHdr.NLevels;CurrLevel++)
  1062.     {
  1063.         myFRead(&CurrNode,sizeof(BTREEINDEXHEADER),HelpFile);
  1064.         fseek(HelpFile,buffer->FirstLeaf+CurrNode.PreviousPage*(long)BTreeHdr.PageSize,SEEK_SET);
  1065.     }
  1066.     myFRead(&CurrNode,sizeof(CurrNode),HelpFile);
  1067.     buffer->NextPage=CurrNode.NextPage;
  1068.     return CurrNode.NEntries;
  1069. }
  1070.  
  1071. short GetNextPage(FILE *HelpFile,BUFFER *buffer) // walk Btree
  1072. {
  1073.     BTREENODEHEADER CurrNode;
  1074.  
  1075.     if(buffer->NextPage==-1) return 0;
  1076.     fseek(HelpFile,buffer->FirstLeaf+buffer->NextPage*(long)buffer->PageSize,SEEK_SET);
  1077.     myFRead(&CurrNode,sizeof(CurrNode),HelpFile);
  1078.     buffer->NextPage=CurrNode.NextPage;
  1079.     return CurrNode.NEntries;
  1080. }
  1081.  
  1082. void SysLoad(FILE *HelpFile) // gets global values from SYSTEM file
  1083. {
  1084.     FILEHEADER FileHdr;
  1085.     long CurrentLocation;
  1086.     SYSTEMREC SystemRec;
  1087.     long FileOffset;
  1088.     char *data;
  1089.  
  1090.     if(SearchFile(HelpFile,"|SYSTEM",&FileOffset))
  1091.     {
  1092.         fseek(HelpFile,FileOffset,SEEK_SET);
  1093.         myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  1094.         myFRead(&SysHeader,sizeof(SysHeader),HelpFile);
  1095.         before31=SysHeader.Version<3||SysHeader.Version==3&&SysHeader.Revision<16;
  1096.         lzcompressed=!before31&&(SysHeader.Flags==4||SysHeader.Flags==8);
  1097.         if(before31)
  1098.         {
  1099.             DecompressSize=TopicBlockSize=2048;
  1100.         }
  1101.         else if(SysHeader.Flags==8)
  1102.         {
  1103.             TopicBlockSize=2048;
  1104.             DecompressSize=0x4000;
  1105.         }
  1106.         else
  1107.         {
  1108.             TopicBlockSize=4096;
  1109.             DecompressSize=0x4000;
  1110.         }
  1111.         if(before31)
  1112.         {
  1113.             fgets(HelpFileTitle,33,HelpFile);
  1114.         }
  1115.         else  // else get 3.1 System records
  1116.         {
  1117.             CurrentLocation=12;
  1118.             while(CurrentLocation<FileHdr.FileSize)
  1119.             {
  1120.                 myFRead(&SystemRec,sizeof(SYSTEMREC),HelpFile);
  1121.                 CurrentLocation+=4;
  1122.                 if(SystemRec.DataSize)
  1123.                 {
  1124.                     data=myMalloc(SystemRec.DataSize+1);
  1125.                     myFRead(data,SystemRec.DataSize,HelpFile);
  1126.                     data[SystemRec.DataSize]='\0';
  1127.                     CurrentLocation+=SystemRec.DataSize;
  1128.                     switch(SystemRec.RecordType)
  1129.                     {
  1130.                     case 0x0001:
  1131.                         if(*data) strcpy(HelpFileTitle,data);
  1132.                         break;
  1133.                     }
  1134.                     free(data);
  1135.                 }
  1136.             }
  1137.         }
  1138.     }
  1139. }
  1140.  
  1141. /*********************************************************************************
  1142.  
  1143.   GRAPHICS STUFF
  1144.   ==============
  1145.   ExtractBitmap(..) extracts the graphics stored as |bmXXX in the Windows
  1146.   HelpFile to separate files. In this version multiple resolution pictures
  1147.   (with and without hotspots) are saved as MRB's, pictures with hotspots are
  1148.   converted to the SHG-format, single resolution Bitmaps without hotspots
  1149.   to standard BMP-format and all Metafiles without hotspots to the Aldus
  1150.   Placeable Metafile WMF format.
  1151.  
  1152.   GENERAL NOTES ABOUT |bmXXX
  1153.   --------------------------
  1154.  
  1155.   |bmXXX contains one MRB-File. This can be directly recompiled. If hotspots
  1156.   in the graphics contained in the MRB-File shall be added or changed with
  1157.   SHED, the MRB-File has to be split into one SHG file for each graphic. This
  1158.   is very easy as the graphics-data will only be slightly rewrapped. If the
  1159.   graphics themselves shall be edited with a standard drawing program, the
  1160.   SHG files have to be converted into bitmaps or metafiles (as flagged in
  1161.   the SHG). The hotspot data is lost by this step.
  1162.  
  1163.   MRB-FILES
  1164.   ---------
  1165.  
  1166.   MRBC takes the input-file(s), converts them to "lp"-Files if needed, adds
  1167.   a resolution specification if none is given in the file and finally outputs
  1168.   a "lp"-File with the ending ".MRB". Depending on the given display type
  1169.   MRBC sets the following resolutions:
  1170.  
  1171.            |           |               | x-resolution | y-resolution
  1172.   display-type | extension | std-extension |    (ppi)     |    (ppi)
  1173.   -------------+-----------+---------------+--------------+-------------
  1174.        CGA     |   ".C*"   |     ".CGA"    |      96      |      48
  1175.        EGA     |   ".E*"   |     ".EGA"    |      96      |      72
  1176.        VGA     |   ".V*"   |     ".VGA"    |      96      |      96
  1177.        8514    |   ".8*"   |     ".854"    |     120      |     120
  1178.  
  1179.   SHG-Files
  1180.   ---------
  1181.  
  1182.   Structure of SHG-Data:
  1183.  
  1184.    Offset    | Size       | Ref | Description
  1185.    ----------+------------+-----+-----------------------------------------------
  1186.       0      | 1 Byte     |     | always 0x01, maybe a version-number
  1187.       1      | 1 Word     |  N  | the number of hotspots
  1188.       3      | 1 DWord    |  M  | size of macro-data (in bytes)
  1189.       7      | 15xN Bytes | HSD | Hotspot-Data (see below)
  1190.     7 + 15*N | M Bytes    |     | Macro-Data (ASCIIZ-Strings)
  1191.     7+15*N+M | 2*N*ASCIIZ |     | Hotspot-Id and Context- or Macro-Name as
  1192.          |          |    | ASCIIZ-String
  1193.  
  1194.   Structure of Hotspot-Data:
  1195.  
  1196.    Offset    | Size       | Ref | Description
  1197.    ----------+------------+-----+-------------------------------------------------
  1198.       0      | 3 Bytes    |     | Hotspot-Type: 0xE3 0x00 0x00 = jump, visible
  1199.          |          |    |               0xE7 0x04 0x00 = jump, invisible
  1200.          |          |    |               0xE2 0x00 0x00 = pop-up, visible
  1201.          |            |     |               0xE6 0x04 0x00 = pop-up, invisible
  1202.          |            |     |               0xC8 0x00 0x00 = macro, visible
  1203.          |            |     |               0xCC 0x04 0x00 = macro, invisible
  1204.       3      | 1 Word     |     | Left border of hotspot
  1205.       5      | 1 Word     |     | Top border of hotspot
  1206.       7      | 1 Word     |     | Right border - Left border
  1207.       9      | 1 Word     |     | Bottom border - Top border
  1208.      11      | 1 DWord    |     | 0x00000000 => nothing
  1209.          |            |     | 0x00000001 => this is a macro-hotspot
  1210.          |            |     |   others   => hash-value of the context-string
  1211.          |            |     |               according to the WinHelp standard
  1212.  
  1213.   03-06-1995 Holger Haase
  1214. **********************************************************************************/
  1215.  
  1216. MFILE *CreateMap(char *buffer,unsigned int size) // assign a memory mapped file
  1217. {
  1218.     MFILE *f;
  1219.  
  1220.     f=myMalloc(sizeof(MFILE));
  1221.     if(!f) return NULL;
  1222.     f->magic=MAGIC;
  1223.     f->ptr=buffer;
  1224.     f->end=buffer+size;
  1225.     return f;
  1226. }
  1227.  
  1228. char *CloseMap(MFILE *f) // close a memory mapped file (never used)
  1229. {
  1230.     char *ptr;
  1231.  
  1232.     if(!f) return NULL;
  1233.     ptr=f->ptr;
  1234.     free(f);
  1235.     return ptr;
  1236. }
  1237.  
  1238. int GetChar(MFILE *f) // getc from memory mapped file or regular file
  1239. {
  1240.     if(f->magic!=MAGIC) return getc((FILE *)f);
  1241.     if(f->ptr>=f->end) return -1;
  1242.     return *f->ptr++;
  1243. }
  1244.  
  1245. int GetWord(MFILE *f) // getw from memory mapped file or regular file
  1246. {
  1247.     if(f->magic!=MAGIC) return getw((FILE *)f);
  1248.     if(f->ptr>=f->end) return -1;
  1249.     return *((int *)f->ptr)++;
  1250. }
  1251.  
  1252. unsigned short GetCWord(MFILE *f) // get compressed word
  1253. {
  1254.     unsigned char b;
  1255.  
  1256.     b = GetChar(f);
  1257.     if (b & 1) return (((unsigned short)GetChar(f) << 8) | (unsigned short)b) >> 1;
  1258.     return ((unsigned short)b >> 1);
  1259. }
  1260.  
  1261. unsigned long GetCDWord(MFILE *f) // get compressed long
  1262. {
  1263.     unsigned short w;
  1264.  
  1265.     w = GetWord(f);
  1266.     if (w & 1) return (((unsigned long)GetWord(f) << 16) | (unsigned long)w) >> 1;
  1267.     return ((unsigned long)w >> 1);
  1268. }
  1269.  
  1270. unsigned long GetDWord(MFILE *f) // get long
  1271. {
  1272.     unsigned short w;
  1273.  
  1274.     w = GetWord(f);
  1275.     return ((unsigned long)GetWord(f) << 16) | (unsigned long)w;
  1276. }
  1277.  
  1278. long copy(FILE *f,long bytes,FILE *out)
  1279. {
  1280.     long length;
  1281.     int size;
  1282.     static char buffer[1024];
  1283.  
  1284.     for(length=0;length<bytes;length+=size)
  1285.     {
  1286.         size=(int)(bytes-length>sizeof(buffer)?sizeof(buffer):bytes-length);
  1287.         myFRead(buffer,size,f);
  1288.         fwrite(buffer,size,1,out);
  1289.     }
  1290.     return length;
  1291. }
  1292.  
  1293. long CopyBytes(MFILE *f,long bytes,FILE *out)
  1294. {
  1295.     if(f->magic!=MAGIC) return copy((FILE *)f,bytes,out);
  1296.     if(bytes>(size_t)(f->end-f->ptr)) bytes=(size_t)(f->end-f->ptr);
  1297.     fwrite(f->ptr,(size_t)bytes,1,out);
  1298.     f->ptr+=(size_t)bytes;
  1299.     return bytes;
  1300. }
  1301.  
  1302. void derun(int i,FILE *f) // expand runlen compressed data
  1303. {
  1304.     static signed char count;
  1305.     static char runlen;
  1306.  
  1307.     if(!f)
  1308.     {
  1309.         count=0;
  1310.         runlen=i;
  1311.     }
  1312.     else if(runlen)
  1313.     {
  1314.         if(count&0x7F)
  1315.         {
  1316.             do
  1317.             {
  1318.                 putc(i,f);
  1319.                 count--;
  1320.             }
  1321.             while(count>0);
  1322.         }
  1323.         else
  1324.         {
  1325.             count=(signed char)i;
  1326.         }
  1327.     }
  1328.     else
  1329.     {
  1330.         putc(i,f);
  1331.     }
  1332. }
  1333.  
  1334. // decode bytes from file f into file out or into buffer
  1335. unsigned short LzExpand(MFILE *f,long bytes,FILE *out,unsigned char *buffer)
  1336. {
  1337.     static unsigned char lzbuffer[0x1000];
  1338.     unsigned char b; // bits read from f signalling compressed/uncompressed
  1339.     unsigned char c; // decompressed char
  1340.     unsigned char m; // mask to test bit in b
  1341.     int l;
  1342.     unsigned int i; // where we are
  1343.     unsigned int p; // word read from f / from where we copy
  1344.  
  1345.     i=0;
  1346.     derun(out!=NULL&&buffer!=NULL,NULL);
  1347.     while(bytes>0L) // Go through until we're done
  1348.     {
  1349.         b=GetChar(f);
  1350.         bytes--;
  1351.         if(bytes<=0L) break;
  1352.         for(m=0x01;m;m<<=1)
  1353.         {
  1354.             if(b&m) // bit set: take doublebyte, decode and copy
  1355.             {
  1356.                 p=GetWord(f);
  1357.                 l=((p>>12)&15)+3;
  1358.                 p=i-(p&0xFFF)-1;
  1359.                 while(l--)
  1360.                 {
  1361.                     lzbuffer[i++&0xFFF]=c=lzbuffer[p++&0xFFF];
  1362.                     if(out) derun(c,out); else *buffer++=c;
  1363.                 }
  1364.                 bytes-=2;
  1365.             }
  1366.             else // bit clear: take byte and store
  1367.             {
  1368.                 lzbuffer[i++&0xFFF]=c=GetChar(f);
  1369.                 if(out) derun(c,out); else *buffer++=c;
  1370.                 bytes--;
  1371.             }
  1372.             if(bytes<=0L) break; // stop if all bytes are consumed
  1373.         }
  1374.     }
  1375.     return i;
  1376. }
  1377.  
  1378. long DecodeData(MFILE *f,unsigned long dwDataSize,FILE *fTarget,unsigned long dwRawSize,unsigned char byPacked)
  1379. {
  1380.     unsigned long dwTargetSize;
  1381.     long start;
  1382.     unsigned char count,value;
  1383.  
  1384.     start=ftell(fTarget);
  1385.     switch(byPacked)
  1386.     {
  1387.     case 0: // unpacked
  1388.         CopyBytes(f,dwDataSize,fTarget);
  1389.         break;
  1390.     case 1: // runlen
  1391.         while(dwDataSize>0)
  1392.         {
  1393.             count=GetChar(f);
  1394.             dwDataSize--;
  1395.             if(count&0x80)
  1396.             {
  1397.                 count&=0x7F;
  1398.                 dwDataSize-=count;
  1399.                 CopyBytes(f,count,fTarget);
  1400.             }
  1401.             else
  1402.             {
  1403.                 dwDataSize--;
  1404.                 value=GetChar(f);
  1405.                 while(count-->0) putc(value,fTarget);
  1406.             }
  1407.         }
  1408.         break;
  1409.     case 2: // LZ77
  1410.         LzExpand(f,dwDataSize,fTarget,NULL);
  1411.         break;
  1412.     case 3: // both
  1413.         LzExpand(f,dwDataSize,fTarget,buffer);
  1414.         break;
  1415.     default:
  1416.         error("unknown packing method: %02X.\n",byPacked);
  1417.         return 0L;
  1418.     }
  1419.     dwTargetSize=ftell(fTarget)-start;
  1420.     if(dwTargetSize<dwRawSize)
  1421.     {
  1422.         error("Picture decompression produced %lu bytes instead of required %lu\n",dwTargetSize,dwRawSize);
  1423.     }
  1424.     else if(dwTargetSize>dwRawSize)
  1425.     {
  1426.         fprintf(stderr,"Picture decompression produced %ld bytes more than required.\n",dwTargetSize-dwRawSize);
  1427.     }
  1428.     return dwTargetSize;
  1429. }
  1430.  
  1431. void ExtractMacros(char *filename)
  1432. {
  1433.     FILE *f;
  1434.     long dwHotspotOffset;
  1435.     int ch;
  1436.     unsigned int count,i,n,j,hotspots,k;
  1437.     long pos,length;
  1438.     unsigned char byType;
  1439.     HOTSPOT *hotspot;
  1440.     static char buffer[1024];
  1441.     char *ptr;
  1442.  
  1443.     f=fopen(filename,"rb");
  1444.     if(f)
  1445.     {
  1446.         if(((MFILE *)f)->magic==MAGIC)
  1447.         {
  1448.             error("Error opening '%s'\n",filename);
  1449.         }
  1450.         else
  1451.         {
  1452.             getw(f);
  1453.             count=getw(f);
  1454.             for(i=0;i<count;i++)
  1455.             {
  1456.                 fseek(f,4+4*i,SEEK_SET);
  1457.                 fread(&pos,1,sizeof(pos),f);
  1458.                 fseek(f,pos,SEEK_SET);
  1459.                 byType=getc(f); // type of picture: 5=DDB, 6=DIB, 8=METAFILE
  1460.                 j=getc(f); // packing method: 0=unpacked, 1=RunLen, 2=LZ77, 3=both
  1461.                 if(byType!=8&&byType!=6&&byType!=5)
  1462.                 {
  1463.                     fprintf(stderr,"Unknown FileType %d\n",byType);
  1464.                 }
  1465.                 else
  1466.                 {
  1467.                     if(byType==8)
  1468.                     {
  1469.                         GetCWord((MFILE *)f); // mapping mode
  1470.                         getw(f); // width of metafile-picture
  1471.                         getw(f); // height of metafile-picture
  1472.                         GetCDWord((MFILE *)f); // dwDataSize
  1473.                     }
  1474.                     else
  1475.                     {
  1476.                         GetCDWord((MFILE *)f); // biXPelsPerMeter/(100/2.54)
  1477.                         GetCDWord((MFILE *)f); // biYPelsPerMeter/(100/2.54)
  1478.                         GetCWord((MFILE *)f); // biPlanes
  1479.                         GetCWord((MFILE *)f); // biBitCount
  1480.                         GetCDWord((MFILE *)f); // biWidth
  1481.                         GetCDWord((MFILE *)f); // biHeight
  1482.                         GetCDWord((MFILE *)f); // biClrUsed
  1483.                         GetCDWord((MFILE *)f); // biClrImportant
  1484.                     }
  1485.                     GetCDWord((MFILE *)f); // dwRawSize
  1486.                     GetCDWord((MFILE *)f); // dwHotspotSize
  1487.                     GetDWord((MFILE *)f); // dwPictureOffset
  1488.                     dwHotspotOffset=GetDWord((MFILE *)f);
  1489.                     if(dwHotspotOffset)
  1490.                     {
  1491.                         fseek(f,pos+dwHotspotOffset,SEEK_SET);
  1492.                         if(getc(f)!=-1)
  1493.                         {
  1494.                             hotspots=getw(f);
  1495.                             fread(&length,1,sizeof(length),f);
  1496.                             hotspot=myMalloc(hotspots*sizeof(HOTSPOT));
  1497.                             fread(hotspot,sizeof(HOTSPOT),hotspots,f);
  1498.                             myFRead(buffer,length,f);
  1499.                             for(n=0;n<hotspots;n++) if((hotspot[n].id0&0xFB)==0xC8) // skip macros
  1500.                             {
  1501.                                 for(j=0;j<sizeof(buffer)-1&&(ch=getc(f))>0;j++) buffer[j]=ch;
  1502.                                 buffer[j]='\0';
  1503.                             }
  1504.                             for(n=0;n<hotspots;n++)
  1505.                             {
  1506.                                 for(j=0;j<sizeof(buffer)-1&&(ch=getc(f))>0;j++) buffer[j]=ch;
  1507.                                 buffer[j++]='\0';
  1508.                                 for(k=j;k<sizeof(buffer)-1&&(ch=getc(f))>0;k++) buffer[k]=ch;
  1509.                                 buffer[k]='\0';
  1510.                                 switch(hotspot[n].id0)
  1511.                                 {
  1512.                                 case 0xC8: // macro (never seen)
  1513.                                 case 0xCC: // macro without font change
  1514.                                     AddMacro(buffer+j);
  1515.                                     break;
  1516.                                 case 0xE0: // popup jump HC30
  1517.                                 case 0xE1: // topic jump HC30
  1518.                                 case 0xE2: // popup jump HC31
  1519.                                 case 0xE3: // topic jump HC31
  1520.                                 case 0xE6: // popup jump without font change
  1521.                                 case 0xE7: // topic jump without font change
  1522.                                     if(hash(buffer+j)!=hotspot[n].hash)
  1523.                                     {
  1524.                                         fprintf(stderr,"Wrong hash %08lx instead %08lx for '%s'\n",hotspot[n].hash,hash(buffer+j),buffer+j);
  1525.                                     }
  1526.                                     AddTopic(buffer+j);
  1527.                                     break;
  1528.                                 case 0xEA: // popup jump into external file
  1529.                                 case 0xEB: // topic jump into external file / secondary window
  1530.                                 case 0xEE: // popup jump into external file without font change
  1531.                                 case 0xEF: // topic jump into external file / secondary window without font change
  1532.                                     if(hotspot[n].id1!=0&&hotspot[n].id1!=1&&hotspot[n].id1!=4&&hotspot[n].id1!=6||hotspot[n].id2!=0)
  1533.                                     {
  1534.                                     }
  1535.                                     else
  1536.                                     {
  1537.                                         if(strchr(buffer+j,'@')==NULL) // no external file specified
  1538.                                         {
  1539.                                             ptr=strchr(buffer+j,'>');
  1540.                                             if(ptr) *ptr='\0';
  1541.                                             AddTopic(buffer+j);
  1542.                                         }
  1543.                                         break;
  1544.                                     }
  1545.                                 default:
  1546.                                     error("Unknown hotspot %02x %02x %02x X=%u Y=%u W=%u H=%u %08lx,%s,%s\n",hotspot[n].id0,hotspot[n].id1,hotspot[n].id2,hotspot[n].x,hotspot[n].y,hotspot[n].w,hotspot[n].h,hotspot[n].hash,buffer,buffer+j);
  1547.                                 }
  1548.                             }
  1549.                             free(hotspot);
  1550.                         }
  1551.                         else
  1552.                         {
  1553.                             fprintf(stderr,"No hotspots\n");
  1554.                         }
  1555.                     }
  1556.                 }
  1557.             }
  1558.         }
  1559.         fclose(f);
  1560.     }
  1561.     else
  1562.     {
  1563.         error("Can't extract macros from %s. Open failed.\n",filename);
  1564.     }
  1565. }
  1566.  
  1567. int GetPackedByte(MFILE *f) // RulLen decompression
  1568. {
  1569.     static unsigned char count,value;
  1570.  
  1571.     if(!f) // initialize
  1572.     {
  1573.         count=value=0;
  1574.     }
  1575.     else
  1576.     {
  1577.         if((count&0x7F)==0)
  1578.         {
  1579.             count=GetChar(f);
  1580.             value=GetChar(f);
  1581.         }
  1582.         else if(count&0x80)
  1583.         {
  1584.             value=GetChar(f);
  1585.         }
  1586.         count--;
  1587.     }
  1588.     return value;
  1589. }
  1590.  
  1591. BOOL CheckHash(char *filename,long hash)
  1592. {
  1593.     static char *notfound[20];
  1594.     static int notfounds=0;
  1595.     FILE *f;
  1596.     long offset;
  1597.     int i,j,k,l,n;
  1598.     FILEHEADER FileHdr;
  1599.     BTREEHEADER BTreeHdr;
  1600.     CONTEXTREC ContextRec;
  1601.  
  1602.     // open file and check if it is a Windows help file
  1603.     f=fopen(filename,"rb");
  1604.     if(!f)
  1605.     {
  1606.         for(i=0;i<notfounds;i++) if(strcmp(notfound[i],filename)==0) return FALSE;
  1607.         fprintf(stderr,"%s not found\n",filename);
  1608.         if(notfounds<sizeof(notfound)/sizeof(notfound[0])) notfound[notfounds++]=strdup(filename);
  1609.         return FALSE;
  1610.     }
  1611.     if(getw(f)==0x5F3F&&getw(f)==0x0003)
  1612.     {
  1613.         // locate the |CONTEXT internal file using the internal directory
  1614.         fread(&offset,sizeof(offset),1,f);
  1615.         fseek(f,offset,SEEK_SET);
  1616.         fread(&FileHdr,sizeof(FileHdr),1,f);
  1617.         fread(&BTreeHdr,sizeof(BTreeHdr),1,f);
  1618.         offset=ftell(f);
  1619.         n=BTreeHdr.RootPage;
  1620.         for(i=1;i<BTreeHdr.NLevels;i++)
  1621.         {
  1622.             fseek(f,offset+n*(long)BTreeHdr.PageSize,SEEK_SET);
  1623.             getw(f);
  1624.             j=getw(f); // NEntries
  1625.             n=getw(f); // PreviousPage
  1626.             for(k=0;k<j;k++)
  1627.             {
  1628.                 for(l=0;(buffer[l]=getc(f))!='\0';l++) ;
  1629.                 if(strcmp("|CONTEXT",buffer)<0) break;
  1630.                 n=getw(f); // Page
  1631.             }
  1632.         }
  1633.         fseek(f,offset+n*(long)BTreeHdr.PageSize,SEEK_SET);
  1634.         getw(f);
  1635.         j=getw(f); // NEntries
  1636.         getw(f); // PreviosPage;
  1637.         getw(f); // NextPage
  1638.         for(i=0;i<j;i++)
  1639.         {
  1640.             for(l=0;(buffer[l]=getc(f))!='\0';l++) ;
  1641.             fread(&offset,sizeof(offset),1,f);
  1642.             if(strcmp("|CONTEXT",buffer)==0)
  1643.             {
  1644.                 // found |CONTEXT in internal directory. Go to this file
  1645.                 // and locate the specified entry
  1646.                 fseek(f,offset,SEEK_SET);
  1647.                 fread(&FileHdr,sizeof(FileHdr),1,f);
  1648.                 fread(&BTreeHdr,sizeof(BTreeHdr),1,f);
  1649.                 offset=ftell(f);
  1650.                 n=BTreeHdr.RootPage;
  1651.                 for(i=1;i<BTreeHdr.NLevels;i++)
  1652.                 {
  1653.                     fseek(f,offset+n*(long)BTreeHdr.PageSize,SEEK_SET);
  1654.                     getw(f);
  1655.                     j=getw(f); // NEntries
  1656.                     n=getw(f); // PreviousPage
  1657.                     for(k=0;k<j;k++)
  1658.                     {
  1659.                         fread(&ContextRec.HashValue,sizeof(ContextRec.HashValue),1,f);
  1660.                         if(ContextRec.HashValue<=hash) break;
  1661.                         n=getw(f); // Page
  1662.                     }
  1663.                 }
  1664.                 fseek(f,offset+n*(long)BTreeHdr.PageSize,SEEK_SET);
  1665.                 getw(f);
  1666.                 j=getw(f); // NEntries
  1667.                 getw(f); // PreviosPage;
  1668.                 getw(f); // NextPage
  1669.                 for(i=0;i<j;i++)
  1670.                 {
  1671.                     fread(&ContextRec,sizeof(ContextRec),1,f);
  1672.                     if(ContextRec.HashValue==hash)
  1673.                     {
  1674.                         fclose(f);
  1675.                         return TRUE;
  1676.                     }
  1677.                     if(ContextRec.HashValue>hash) break;
  1678.                 }
  1679.             }
  1680.         }
  1681.     }
  1682.     else
  1683.     {
  1684.         for(i=0;i<notfounds;i++) if(strcmp(notfound[i],filename)==0) return FALSE;
  1685.         fprintf(stderr,"%s is no valid WinHelp file\n",filename);
  1686.         if(notfounds<sizeof(notfound)/sizeof(notfound[0])) notfound[notfounds++]=strdup(filename);
  1687.     }
  1688.     fclose(f);
  1689.     return FALSE;
  1690. }
  1691.  
  1692. void CheckMacro(char *buffer)
  1693. {
  1694.     fprintf(stderr,"Macro: %s\n",buffer);
  1695. }
  1696.  
  1697. void CheckBitmap(MFILE *f)
  1698. {
  1699.     char byType,byPacked;
  1700.     int n,j;
  1701.     long pos,offset,dwHotspotOffset,dwHotspotSize;
  1702.  
  1703.     pos=myFTell(f);
  1704.     if((GetWord(f)&0xDFFF)==0x506C)
  1705.     {
  1706.         n=GetWord(f);
  1707.         for(j=0;j<n;j++)
  1708.         {
  1709.             myFSeek(f,pos+4*(j+1));
  1710.             offset=GetDWord(f);
  1711.             myFSeek(f,pos+offset);
  1712.             byType=GetChar(f); // type of picture: 5=DDB, 6=DIB, 8=METAFILE
  1713.             byPacked=GetChar(f); // packing method: 0=unpacked, 1=RunLen, 2=LZ77
  1714.             dwHotspotOffset=0L;
  1715.             if(byType==6||byType==5&&byPacked<2)
  1716.             {
  1717.                 GetCDWord(f);
  1718.                 GetCDWord(f);
  1719.                 GetCWord(f);
  1720.                 GetCWord(f);
  1721.                 GetCDWord(f);
  1722.                 GetCDWord(f);
  1723.                 GetCDWord(f);
  1724.                 GetCDWord(f);
  1725.                 GetCDWord(f);
  1726.                 dwHotspotSize=GetCDWord(f);
  1727.                 GetDWord(f); // dwPictureOffset
  1728.                 dwHotspotOffset=GetDWord(f);
  1729.             }
  1730.             else if(byType==8) // Windows MetaFile
  1731.             {
  1732.                 GetCWord(f); // mapping mode
  1733.                 GetWord(f); // width of metafile-picture
  1734.                 GetWord(f); // height of metafile-picture
  1735.                 GetCDWord(f);
  1736.                 GetCDWord(f);
  1737.                 dwHotspotSize=GetCDWord(f); // dwHotspotSize
  1738.                 GetDWord(f); // dwPictureOffset
  1739.                 dwHotspotOffset=GetDWord(f);
  1740.             }
  1741.             if(dwHotspotOffset) // dwHotspotOffset
  1742.             {
  1743.                 myFSeek(f,offset+dwHotspotOffset);
  1744.                 if(GetChar(f)==1)
  1745.                 {
  1746.                     int i,l,n;
  1747.                     HOTSPOT *hotspot;
  1748.  
  1749.                     n=GetWord(f);
  1750.                     if(n>0)
  1751.                     {
  1752.                         hotspot=malloc(n*sizeof(HOTSPOT));
  1753.                         if(hotspot)
  1754.                         {
  1755.                             GetWord(f);
  1756.                             GetWord(f);
  1757.                             for(i=0;i<n*sizeof(HOTSPOT);i++) ((char *)hotspot)[i]=GetChar(f);
  1758.                             for(i=0;i<n;i++) if((hotspot[i].id0&0xFB)==0xC8) // skip macros
  1759.                             {
  1760.                                 for(l=0;l<sizeof(buffer)&&(buffer[l]=GetChar(f))!='\0';l++); // skip macro
  1761.                             }
  1762.                             for(i=0;i<n;i++)
  1763.                             {
  1764.                                 char *filename;
  1765.                                 char *ptr;
  1766.  
  1767.                                 for(l=0;l<sizeof(buffer)&&(buffer[l]=GetChar(f))!='\0';l++); // skip name
  1768.                                 for(l=0;l<sizeof(buffer)&&(buffer[l]=GetChar(f))!='\0';l++);
  1769.                                 switch(hotspot[i].id0)
  1770.                                 {
  1771.                                 case 0xC8: // macro (never seen)
  1772.                                 case 0xCC: // macro without font change
  1773.                                     CheckMacro(buffer);
  1774.                                     break;
  1775.                                 case 0xEA:
  1776.                                 case 0xEB:
  1777.                                 case 0xEE:
  1778.                                 case 0xEF:
  1779.                                     filename=strchr(buffer,'@');
  1780.                                     if(filename) // external file
  1781.                                     {
  1782.                                         *filename++='\0';
  1783.                                         ptr=strchr(buffer,'>');
  1784.                                         if(ptr) *ptr='\0';
  1785.                                         if(!CheckHash(filename,hash(buffer)))
  1786.                                         {
  1787.                                             fprintf(stderr,"%s@%s not satisfied\n",buffer,filename);
  1788.                                         }
  1789.                                     }
  1790.                                 }
  1791.                             }
  1792.                             free(hotspot);
  1793.                         }
  1794.                     }
  1795.                 }
  1796.             }
  1797.         }
  1798.     }
  1799. }
  1800.  
  1801. int ExtractBitmap(char *szFilename,MFILE *f,long FileSize)
  1802. {
  1803.     unsigned char byType;
  1804.     unsigned char byPacked;
  1805.     long l;
  1806.     BITMAPFILEHEADER bmfh;
  1807.     BITMAPINFOHEADER bmih;
  1808.     int colors;
  1809.     long FileStart;
  1810.     unsigned short wMagic;
  1811.     unsigned long dwRawSize,dwDataSize,dwHotspotOffset,dwOffsBitmap;
  1812.     unsigned int i;
  1813.     FILE *fTarget;
  1814.     int type;
  1815.  
  1816.     FileStart=myFTell(f);
  1817.     wMagic=GetWord(f);
  1818.     if((wMagic&0xDFFF)!=0x506C)
  1819.     {
  1820.         fprintf(stderr,"Unknown bitmap wMagic %04X (%c%c)\n",wMagic,wMagic&0x00FF,wMagic>>8);
  1821.         // output file for debugging
  1822.         fTarget=myFOpen(szFilename,"wb");
  1823.         if(fTarget)
  1824.         {
  1825.             putw(wMagic,fTarget);
  1826.             CopyBytes(f,FileSize-2,fTarget);
  1827.             myFClose(fTarget);
  1828.         }
  1829.         return 0;
  1830.     }
  1831.     // wMagic & 0x2000 unknown
  1832.     if(GetWord(f)>1) // number of bitmaps (MultiResolutionBitmap)
  1833.     {
  1834.         strcat(szFilename,".MRB");
  1835.         type=3;
  1836.     }
  1837.     else
  1838.     {
  1839.         dwOffsBitmap=GetDWord(f);
  1840.         myFSeek(f,FileStart+dwOffsBitmap);
  1841.         byType=GetChar(f); // type of picture: 5=DDB, 6=DIB, 8=METAFILE
  1842.         byPacked=GetChar(f); // packing method: 0=unpacked, 1=RunLen, 2=LZ77
  1843.         if(byType==6||byType==5&&byPacked<2)
  1844.         {
  1845.             memset(&bmfh,0,sizeof(bmfh));
  1846.             memset(&bmih,0,sizeof(bmih));
  1847.             bmfh.bfType=0x4D42; // bitmap magic ("BM")
  1848.             bmih.biSize=sizeof(bmih);
  1849.             // HC30 doesn't like certain PelsPerMeter, so let them be 0
  1850.             /* bmih.biXPelsPerMeter = 40L* */ GetCDWord(f);
  1851.             /* bmih.biYPelsPerMeter = 40L* */ GetCDWord(f);
  1852.             bmih.biPlanes=GetCWord(f);
  1853.             bmih.biBitCount=GetCWord(f);
  1854.             bmih.biWidth=GetCDWord(f);
  1855.             bmih.biHeight=GetCDWord(f);
  1856.             colors=(int)(bmih.biClrUsed=GetCDWord(f));
  1857.             if(!colors) colors=1<<bmih.biBitCount;
  1858.             bmih.biClrImportant=GetCDWord(f);
  1859.             dwDataSize=GetCDWord(f);
  1860.             GetCDWord(f); // dwHotspotSize
  1861.             GetDWord(f); // dwPictureOffset
  1862.             dwHotspotOffset=GetDWord(f);
  1863.             if(!dwHotspotOffset)
  1864.             {
  1865.                 strcat(szFilename,".BMP");
  1866.                 fTarget=myFOpen(szFilename,"wb");
  1867.                 if(fTarget)
  1868.                 {
  1869.                     fwrite(&bmfh,1,sizeof(bmfh),fTarget);
  1870.                     fwrite(&bmih,1,sizeof(bmih),fTarget);
  1871.                     if(byType==6)
  1872.                     {
  1873.                         CopyBytes(f,colors*4L,fTarget);
  1874.                     }
  1875.                     else
  1876.                     {
  1877.                         l=0x000000L;
  1878.                         fwrite(&l,1,sizeof(l),fTarget);
  1879.                         l=0xFFFFFFL;
  1880.                         fwrite(&l,1,sizeof(l),fTarget);
  1881.                     }
  1882.                     bmfh.bfOffBits=sizeof(bmfh)+sizeof(bmih)+colors*4L;
  1883.                     bmih.biSizeImage=(((bmih.biWidth*bmih.biBitCount+31)/32)*4)*bmih.biHeight;
  1884.                     if(byType==5) // convert 3.0 DDB to 3.1 DIB
  1885.                     {
  1886.                         long width,length;
  1887.                         int pad;
  1888.  
  1889.                         width=((bmih.biWidth*bmih.biBitCount+15)/16)*2;
  1890.                         pad=(int)(((width+3)/4)*4-width);
  1891.                         GetPackedByte(NULL);
  1892.                         for(l=0;l<bmih.biHeight;l++)
  1893.                         {
  1894.                             if(byPacked==1)
  1895.                             {
  1896.                                 for(length=0;length<width;length++) putc(GetPackedByte(f),fTarget);
  1897.                             }
  1898.                             else
  1899.                             {
  1900.                                 CopyBytes(f,width,fTarget);
  1901.                             }
  1902.                             if(pad) fwrite(buffer,pad,1,fTarget);
  1903.                         }
  1904.                     }
  1905.                     else
  1906.                     {
  1907.                         DecodeData(f,dwDataSize,fTarget,bmih.biSizeImage,byPacked);
  1908.                     }
  1909.                     // update bitmap headers
  1910.                     bmfh.bfSize=ftell(fTarget);
  1911.                     fseek(fTarget,0L,SEEK_SET);
  1912.                     fwrite(&bmfh,1,sizeof(bmfh),fTarget);
  1913.                     fwrite(&bmih,1,sizeof(bmih),fTarget);
  1914.                     myFClose(fTarget);
  1915.                 }
  1916.                 if(bmih.biPlanes==1&&bmih.biBitCount==4&&bmih.biClrImportant==1) return 5;
  1917.                 return 1;
  1918.             }
  1919.             strcat(szFilename,".SHG");
  1920.             type=2;
  1921.         }
  1922.         else if(byType==8) // Windows MetaFile
  1923.         {
  1924.             APMFILEHEADER afh;
  1925.             unsigned short *wp;
  1926.  
  1927.             memset(&afh,0,sizeof(afh));
  1928.             afh.dwKey=0x9AC6CDD7L;
  1929.             GetCWord(f); // mapping mode
  1930.             afh.rcBBox.right=GetWord(f); // width of metafile-picture
  1931.             afh.rcBBox.bottom=GetWord(f); // height of metafile-picture
  1932.             dwRawSize=GetCDWord(f);
  1933.             dwDataSize=GetCDWord(f);
  1934.             GetCDWord(f); // dwHotspotSize
  1935.             GetDWord(f); // dwPictureOffset
  1936.             dwHotspotOffset=GetDWord(f);
  1937.             if(!dwHotspotOffset)
  1938.             {
  1939.                 afh.wInch=2540;
  1940.                 wp=(unsigned short *)&afh;
  1941.                 for(i=0;i<10;i++) afh.wChecksum^=*wp++;
  1942.                 strcat(szFilename,".WMF");
  1943.                 fTarget=myFOpen(szFilename,"wb");
  1944.                 if(fTarget)
  1945.                 {
  1946.                     fwrite(&afh,1,sizeof(afh),fTarget);
  1947.                     DecodeData(f,dwDataSize,fTarget,dwRawSize,byPacked);
  1948.                     myFClose(fTarget);
  1949.                 }
  1950.                 return 4;
  1951.             }
  1952.             strcat(szFilename,".SHG");
  1953.             type=6;
  1954.         }
  1955.         else
  1956.         {
  1957.             fprintf(stderr,"Unknown byType %02x byPacked=%02x. %s skipped.\n",byType,byPacked,szFilename);
  1958.             return 0;
  1959.         }
  1960.     }
  1961.     myFSeek(f,FileStart);
  1962.     fTarget=myFOpen(szFilename,"wb");
  1963.     if(fTarget)
  1964.     {
  1965.         CopyBytes(f,FileSize,fTarget);
  1966.         myFClose(fTarget);
  1967.         if(extractmacros) ExtractMacros(szFilename);
  1968.     }
  1969.     return type; // 2 = SHG, 3 = MRB, 6 = SHG Metafile
  1970. }
  1971. //***************************************************************************
  1972. // END OF GRAPHICS STUFF
  1973. //***************************************************************************
  1974.  
  1975. void ListFiles(FILE *HelpFile)
  1976. {
  1977.     BUFFER buf;
  1978.     char FileName[20];
  1979.     long FileOffset;
  1980.     int n,i,entries;
  1981.  
  1982.     printf("FileName                FileOffset | FileName                FileOffset\n");
  1983.     printf("-----------------------------------+-----------------------------------\n");
  1984.     entries=GetFirstPage(HelpFile,HelpHeader.DirectoryStart,&buf);
  1985.     n=0;
  1986.     while(entries)
  1987.     {
  1988.         for(i=0;i<entries;i++)
  1989.         {
  1990.             StringRead(FileName,sizeof(FileName),HelpFile);
  1991.             myFRead(&FileOffset,sizeof(FileOffset),HelpFile);
  1992.             printf("%-23s 0x%08lX %s",FileName,FileOffset,(n%2?"\n":"| "));
  1993.             n++;
  1994.         }
  1995.         entries=GetNextPage(HelpFile,&buf);
  1996.     }
  1997.     if(n%2) printf("\n");
  1998. }
  1999.  
  2000. char *getbitmapname(unsigned int n)
  2001. {
  2002.     static char name[12];
  2003.  
  2004.     if(n<extensions&&extension[n])
  2005.     {
  2006.         sprintf(name,"bm%u.%s",n,bmpext[extension[n]&0x0F]);
  2007.     }
  2008.     else if(n==65535U)
  2009.     {
  2010.         missing++;
  2011.         fprintf(stderr,"\nThere was a picture file missing on creation of helpfile.\n");
  2012.         strcpy(name,"missing.bmp");
  2013.     }
  2014.     else
  2015.     {
  2016.         warnings=TRUE;
  2017.         if(warn) fprintf(stderr,"\nBitmap bm%u not exported\n",n);
  2018.         sprintf(name,"bm%u.bmp",n);
  2019.     }
  2020.     return name;
  2021. }
  2022.  
  2023. void ListBitmaps(FILE *hpj)
  2024. {
  2025.     unsigned int n;
  2026.  
  2027.     if(hpj&&extensions)
  2028.     {
  2029.         fprintf(hpj,"[BITMAPS]\n");
  2030.         for(n=0;n<extensions;n++) if(extension[n])
  2031.         {
  2032.             fprintf(hpj,"bm%u.%s\n",n,bmpext[extension[n]&0x0F]);
  2033.         }
  2034.         fprintf(hpj,"\n");
  2035.     }
  2036. }
  2037.  
  2038. void ExportBitmaps(FILE *HelpFile)
  2039. {
  2040.     BUFFER buf;
  2041.     FILEHEADER FileHdr;
  2042.     char *leader;
  2043.     char FileName[20];
  2044.     long FileOffset;
  2045.     int i,j,entries,type;
  2046.     long savepos;
  2047.  
  2048.     leader="|bm"+before31;
  2049.     entries=GetFirstPage(HelpFile,HelpHeader.DirectoryStart,&buf);
  2050.     while(entries)
  2051.     {
  2052.         for(i=0;i<entries;i++)
  2053.         {
  2054.             StringRead(FileName,sizeof(FileName),HelpFile);
  2055.             myFRead(&FileOffset,sizeof(FileOffset),HelpFile);
  2056.             if(memcmp(FileName,leader,strlen(leader))==0)
  2057.             {
  2058.                 savepos=ftell(HelpFile);
  2059.                 fseek(HelpFile,FileOffset,SEEK_SET);
  2060.                 fread(&FileHdr,sizeof(FileHdr),1,HelpFile);
  2061.                 if(checkexternal)
  2062.                 {
  2063.                     CheckBitmap((MFILE *)HelpFile);
  2064.                 }
  2065.                 else
  2066.                 {
  2067.                     type=ExtractBitmap(FileName+(FileName[0]=='|'),(MFILE *)HelpFile,FileHdr.FileSize);
  2068.                     if(type)
  2069.                     {
  2070.                         j=atoi(FileName+(FileName[0]=='|')+2);
  2071.                         if(j>=extensions)
  2072.                         {
  2073.                             extension=realloc(extension,(j+1)*sizeof(char));
  2074.                             while(extensions<=j) extension[extensions++]=0;
  2075.                         }
  2076.                         extension[j]=type;
  2077.                     }
  2078.                 }
  2079.                 fseek(HelpFile,savepos,SEEK_SET);
  2080.             }
  2081.         }
  2082.         entries=GetNextPage(HelpFile,&buf);
  2083.     }
  2084. }
  2085.  
  2086. void ListBaggage(FILE *HelpFile,FILE *hpj)
  2087. {
  2088.     BOOL headerwritten;
  2089.     char *leader;
  2090.     char FileName[20];
  2091.     long FileOffset;
  2092.     BUFFER buf;
  2093.     int i,entries;
  2094.     FILEHEADER FileHdr;
  2095.     FILE *f;
  2096.     long savepos;
  2097.  
  2098.     if(hpj)
  2099.     {
  2100.         headerwritten=FALSE;
  2101.         leader="|bm"+before31;
  2102.         entries=GetFirstPage(HelpFile,HelpHeader.DirectoryStart,&buf);
  2103.         while(entries)
  2104.         {
  2105.             for(i=0;i<entries;i++)
  2106.             {
  2107.                 StringRead(FileName,sizeof(FileName),HelpFile);
  2108.                 myFRead(&FileOffset,sizeof(FileOffset),HelpFile);
  2109.                 if(FileName[0]!='|'&&memcmp(FileName,leader,strlen(leader))!=0&&!strstr(FileName,".GRP"))
  2110.                 {
  2111.                     if(!headerwritten)
  2112.                     {
  2113.                         fprintf(hpj,"[BAGGAGE]\n");
  2114.                         headerwritten=TRUE;
  2115.                     }
  2116.                     savepos=ftell(HelpFile);
  2117.                     fseek(HelpFile,FileOffset,SEEK_SET);
  2118.                     myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  2119.                     fprintf(hpj,"%s\n",FileName);
  2120.                     f=myFOpen(FileName,"wb");
  2121.                     if(f)
  2122.                     {
  2123.                         copy(HelpFile,FileHdr.FileSize,f);
  2124.                         myFClose(f);
  2125.                     }
  2126.                     fseek(HelpFile,savepos,SEEK_SET);
  2127.                 }
  2128.             }
  2129.             entries=GetNextPage(HelpFile,&buf);
  2130.         }
  2131.         if(headerwritten) fprintf(hpj,"\n");
  2132.     }
  2133. }
  2134.  
  2135. void HexDump(FILE *HelpFile,long FileStart)
  2136. {
  2137.     FILEHEADER  FileHdr;
  2138.     char Buffer[16];
  2139.     long i;
  2140.     int BytesToPrint,Index;
  2141.  
  2142.     fseek(HelpFile,FileStart,SEEK_SET);
  2143.     myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  2144.     printf("File Size: 0x%08lX\n\n",FileHdr.FileSize);
  2145.     printf("Offset                   Hex Values                           Ascii\n");
  2146.     printf("-------------------------------------------------------------------------\n");
  2147.     for(i=0;i<FileHdr.FileSize;i+=16)
  2148.     {
  2149.         printf("0x%08lX: ",i);
  2150.         BytesToPrint=(int)(FileHdr.FileSize-i>16?16:FileHdr.FileSize-i);
  2151.         myFRead(Buffer,BytesToPrint,HelpFile);
  2152.         for(Index=0;Index<BytesToPrint;Index++) printf("%02X ",(unsigned char) Buffer[Index]);
  2153.         for(Index=0;Index<16-BytesToPrint;Index++) printf("   ");
  2154.         for(Index=0;Index<BytesToPrint;Index++)
  2155.         {
  2156.             putchar(isprint((unsigned char)Buffer[Index])?Buffer[Index]:'.');
  2157.         }
  2158.         putchar('\n');
  2159.     }
  2160. }
  2161.  
  2162. void HexDumpMemory(unsigned char *bypMem,long lAnz)
  2163. {
  2164.     long i;
  2165.     int BytesToPrint,Index;
  2166.  
  2167.     printf("\nOffset                   Hex Values                           Ascii\n");
  2168.     printf("-------------------------------------------------------------------------\n");
  2169.     for(i=0;i<lAnz;i+=16)
  2170.     {
  2171.         printf("0x%08lX: ",i);
  2172.         BytesToPrint=(int)(lAnz-i>16?16:lAnz-i);
  2173.         for(Index=0;Index<BytesToPrint;Index++) printf("%02X ",(unsigned char)bypMem[Index]);
  2174.         for(Index=0;Index<16-BytesToPrint;Index++) printf("   ");
  2175.         for(Index=0;Index<BytesToPrint;Index++) putchar(isprint((unsigned char)bypMem[Index])?bypMem[Index]:'.');
  2176.         putchar('\n');
  2177.         bypMem+=16;
  2178.     }
  2179.     putchar('\n');
  2180. }
  2181.  
  2182. void PhrImageDump(FILE *HelpFile,long FileStart)
  2183. {
  2184.     FILEHEADER FileHdr;
  2185.     PHRINDEXHDR PhrIndexHdr;
  2186.     unsigned short bytes;
  2187.     unsigned char *buffer;
  2188.  
  2189.     if(SearchFile(HelpFile,"|PhrIndex",&FileStart))
  2190.     {
  2191.         fseek(HelpFile,FileStart,SEEK_SET);
  2192.         myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  2193.         myFRead(&PhrIndexHdr,sizeof(PhrIndexHdr),HelpFile);
  2194.         if(SearchFile(HelpFile,"|PhrImage",&FileStart))
  2195.         {
  2196.             if(PhrIndexHdr.phrimagesize==PhrIndexHdr.phrimagecompressedsize)
  2197.             {
  2198.                 HexDump(HelpFile,FileStart);
  2199.             }
  2200.             else
  2201.             {
  2202.                 fseek(HelpFile,FileStart,SEEK_SET);
  2203.                 myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  2204.                 if(FileHdr.FileSize!=PhrIndexHdr.phrimagecompressedsize)
  2205.                 {
  2206.                     fprintf(stderr,"PhrImage FileSize %ld, in PhrIndex.FileHdr %ld\n",PhrIndexHdr.phrimagecompressedsize,FileHdr.FileSize);
  2207.                 }
  2208.                 buffer=myMalloc(PhrIndexHdr.phrimagesize);
  2209.                 bytes=LzExpand((MFILE *)HelpFile,FileHdr.FileSize,NULL,buffer);
  2210.                 if(bytes!=PhrIndexHdr.phrimagesize)
  2211.                 {
  2212.                     fprintf(stderr,"PhrImage Size %ld, in PhrIndex %ld\n",PhrIndexHdr.phrimagesize,FileHdr.FileSize);
  2213.                 }
  2214.                 HexDumpMemory(buffer,bytes);
  2215.                 free(buffer);
  2216.             }
  2217.         }
  2218.     }
  2219. }
  2220.  
  2221. char *TopicName(long topic,BOOL removeduplicates)
  2222. {
  2223.     static char buffer[20];
  2224.     int i;
  2225.  
  2226.     if(before31)
  2227.     {
  2228.         if(topic==0L) topic=Topic[0];
  2229.         for(i=16;i<Topics;i++) if(Topic[i]==topic)
  2230.         {
  2231.             sprintf(buffer,"TOPIC%d",i);
  2232.             return buffer;
  2233.         }
  2234.     }
  2235.     else
  2236.     {
  2237.         if(topic==-1L)
  2238.         {
  2239.             NotInAnyTopic=TRUE;
  2240.             return "21KSYK4"; // evaluates to -1 without generating help compiler warning
  2241.         }
  2242.         for(i=0;i<ContextRecs;i++)
  2243.         {
  2244.             if((ContextRec[i].TopicOffset&0x7FFFFFFFL)==topic)
  2245.             {
  2246.                 if(removeduplicates)
  2247.                 {
  2248.                     while(i+1<ContextRecs&&ContextRec[i].TopicOffset==(ContextRec[i+1].TopicOffset|0x80000000L))
  2249.                     {
  2250.                         i++;
  2251.                     }
  2252.                     if(ContextRec[i].TopicOffset&0x80000000L) fprintf(stderr,"Help Compiler will issue warning 3036: The map string \"%s\" has already been used.\n",unhash(ContextRec[i].HashValue));
  2253.                     ContextRec[i].TopicOffset|=0x80000000L;
  2254.                 }
  2255.                 return unhash(ContextRec[i].HashValue);
  2256.             }
  2257.         }
  2258.     }
  2259.     fprintf(stderr,"Can not find topic offset %08lx\n",topic);
  2260.     return NULL;
  2261. }
  2262.  
  2263. void SysDump(FILE *HelpFile,long FileStart)
  2264. {
  2265.     FILEHEADER FileHdr;
  2266.     SYSTEMREC SystemRec;
  2267.     unsigned short CurrentLocation;
  2268.     struct tm *TimeRec;
  2269.     SECWINDOW *SWin;    // Secondary Window record
  2270.     char *data;
  2271.     char *ptr;
  2272.  
  2273.     fseek(HelpFile,FileStart,SEEK_SET);
  2274.     myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  2275.     myFRead(&SysHeader,sizeof(SysHeader),HelpFile);
  2276.     printf("|SYSTEM Dump\n\n");
  2277.     // Figure out Version and Revision
  2278.     if(SysHeader.Version==3&&SysHeader.Revision==15) printf("HC.EXE 3.00 Help Compiler used.\n");
  2279.     else if(SysHeader.Version==3&&SysHeader.Revision==21) printf("HC.EXE 3.10 Help Compiler used.\n");
  2280.     else if(SysHeader.Version==3&&SysHeader.Revision==27) printf("WMVC.EXE Multimedia Compiler used.\n");
  2281.     else if(SysHeader.Version==3&&SysHeader.Revision==33)
  2282.     {
  2283.         if(mvp) printf("MVC.EXE Multimedia Compiler used.\n");
  2284.         else printf("HCW.EXE 4.00 Windows 95 Help Compiler used.\n");
  2285.     }
  2286.     else printf("Unknown Compiler Version %d Revision %02d used!\n",SysHeader.Version,SysHeader.Revision);
  2287.     printf("System Flags & Compression Method=0x%04x\n",SysHeader.Flags);
  2288.     if(SysHeader.Always1!=1) fprintf(stderr,"SysHeader.Always1=%04x\n",SysHeader.Always1);
  2289.     if(SysHeader.Always0!=0) fprintf(stderr,"SysHeader.Always0=%04x\n",SysHeader.Always0);
  2290.     TimeRec=localtime(&SysHeader.GenDate);
  2291.     printf("Help File Generated: %s",asctime(TimeRec));
  2292.     CurrentLocation=12;
  2293.     if(SysHeader.Version<3||SysHeader.Version==3&&SysHeader.Revision<0x10)
  2294.     {
  2295.         fgets(HelpFileTitle,33,HelpFile);
  2296.         printf("TITLE=%s\n",HelpFileTitle);
  2297.     }
  2298.     else while(CurrentLocation<FileHdr.FileSize)
  2299.     {
  2300.         myFRead(&SystemRec,sizeof(SYSTEMREC),HelpFile);
  2301.         data=myMalloc(SystemRec.DataSize+6);
  2302.         myFRead(data,SystemRec.DataSize,HelpFile);
  2303.         data[SystemRec.DataSize]='\0';
  2304.         CurrentLocation=CurrentLocation+4+SystemRec.DataSize;
  2305.         switch(SystemRec.RecordType)
  2306.         {
  2307.         case 0x0001:
  2308.             printf("TITLE=%s\n",data);
  2309.             break;
  2310.         case 0x0002:
  2311.             ptr=strchr(data,'\r');
  2312.             if(ptr) strcpy(ptr,"%date");
  2313.             printf("COPYRIGHT=%s\n",data);
  2314.             break;
  2315.         case 0x0003:
  2316.             printf("CONTENTS=0x%08lX\n",*(long *)data);
  2317.             break;
  2318.         case 0x0004:
  2319.             printf("[MACRO] %s\n",data);
  2320.             break;
  2321.         case 0x0005:
  2322.             printf("Icon in System record\n");
  2323.             break;
  2324.         case 0x0006:
  2325.             SWin=(SECWINDOW *)data;
  2326.             if(SWin->Flags&WSYSFLAG_TYPE) printf("Type: %s\n",SWin->Type);
  2327.             if(SWin->Flags&WSYSFLAG_NAME) printf("Name: %s\n",SWin->Name);
  2328.             if(SWin->Flags&WSYSFLAG_CAPTION) printf("Caption: %s\n",SWin->Caption);
  2329.             if(SWin->Flags&WSYSFLAG_X) printf("X: %d\n",SWin->X);
  2330.             if(SWin->Flags&WSYSFLAG_Y) printf("Y: %d\n",SWin->Y);
  2331.             if(SWin->Flags&WSYSFLAG_WIDTH) printf("Width: %d\n",SWin->Width);
  2332.             if(SWin->Flags&WSYSFLAG_HEIGHT) printf("Height: %d\n",SWin->Height);
  2333.             if(SWin->Maximize) printf("State&Buttons: 0x%04x\n",SWin->Maximize);
  2334.             if(SWin->Flags&WSYSFLAG_RGB) printf("RGB Foreground Colors Set\n");
  2335.             if(SWin->Flags&WSYSFLAG_RGBNSR) printf("RGB For Non-Scrollable Region Set\n");
  2336.             if(SWin->Flags&WSYSFLAG_TOP) printf("Always On Top\n");
  2337.             if(SWin->Flags&WSYSFLAG_AUTOSIZEHEIGHT) printf("Auto-Size Height\n");
  2338.             if(SWin->Flags&0xF000) printf("Additional Flags: 0x%04x\n",SWin->Flags);
  2339.             break;
  2340.         case 0x0008:
  2341.             printf("CITATION=%s\n",data);
  2342.             break;
  2343.         case 0x0009:
  2344.             if(!mvp) printf("LCID=0x%X 0x%X 0x%X\n",*(short *)(data+8),*(short *)data,*(short *)(data+2));
  2345.             break;
  2346.         case 0x000A:
  2347.             if(!mvp) printf("CNT=%s\n",data);
  2348.             break;
  2349.         case 0x000B:
  2350.             if(!mvp) printf("CHARSET=%d\n",*(unsigned char *)(data+1));
  2351.             break;
  2352.         case 0x000C:
  2353.             if(mvp)
  2354.             {
  2355.                 printf("[FTINDEX] dtype %s\n",data);
  2356.             }
  2357.             else
  2358.             {
  2359.                 printf("DEFFONT=%s,%d,%d\n",data+2,*(unsigned char *)data,*(unsigned char *)(data+1));
  2360.             }
  2361.             break;
  2362.         case 0x000D:
  2363.             if(mvp) printf("[GROUPS] %s\n",data);
  2364.             break;
  2365.         case 0x000E:
  2366.             if(mvp)
  2367.             {
  2368.                 printf("[KEYINDEX] keyword=%c, \"%s\"\n",data[1],data+30);
  2369.             }
  2370.             else
  2371.             {
  2372.                 printf("INDEX_SEPARATORS=\"%s\"\n",data);
  2373.             }
  2374.             break;
  2375.         default:
  2376.             fprintf(stderr,"Unknown record type: 0x%04X\n",SystemRec.RecordType);
  2377.         HexDumpMemory(data,SystemRec.DataSize);
  2378.         }
  2379.         free(data);
  2380.     }
  2381. }
  2382.  
  2383. void SysList(FILE *HelpFile,FILE *hpj,char *IconFileName)
  2384. {
  2385.     FILEHEADER FileHdr;
  2386.     SYSTEMREC SystemRec;
  2387.     SECWINDOW *SWin;
  2388.     unsigned short CurrentLocation;
  2389.     long lastpos;
  2390.     char HelpFileTitle[51];
  2391.     long FileStart;
  2392.     char *data;
  2393.     char *ptr;
  2394.     int n;
  2395.     long color;
  2396.     FILE *f;
  2397.     int fbreak,macro,windows,i,dtype,groups,keyword;
  2398.  
  2399.     if(hpj&&SearchFile(HelpFile,"|SYSTEM",&FileStart))
  2400.     {
  2401.         fseek(HelpFile,FileStart,SEEK_SET);
  2402.         myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  2403.         myFRead(&SysHeader,sizeof(SysHeader),HelpFile);
  2404.         if(SysHeader.Version==3&&SysHeader.Revision==15)
  2405.         {
  2406.             strcpy(helpcomp,"HC30");
  2407.         }
  2408.         else if(SysHeader.Version==3&&SysHeader.Revision==21)
  2409.         {
  2410.             strcpy(helpcomp,"HC31");
  2411.         }
  2412.         else if(SysHeader.Version==3&&SysHeader.Revision==27)
  2413.         {
  2414.             strcpy(helpcomp,"WMVC");
  2415.         }
  2416.         else if(SysHeader.Version==3&&SysHeader.Revision==33)
  2417.         {
  2418.             strcpy(helpcomp,mvp?"MVC":"HCW");
  2419.         }
  2420.         fprintf(hpj,"[OPTIONS]\n");
  2421.         if(before31) // If 3.0 get title
  2422.         {
  2423.             fgets(HelpFileTitle,33,HelpFile);
  2424.             if(HelpFileTitle[0]!='\0'&&HelpFileTitle[0]!='\n')
  2425.             {
  2426.                 fprintf(hpj,"TITLE=%s\n",HelpFileTitle);
  2427.                 fprintf(hpj,"INDEX=%s\n",TopicName(0L,FALSE));
  2428.             }
  2429.             if(PhraseCount)
  2430.             {
  2431.                 fprintf(hpj,"COMPRESS=TRUE\n");
  2432.             }
  2433.             else
  2434.             {
  2435.                 fprintf(hpj,"COMPRESS=FALSE\n");
  2436.             }
  2437.             lists['K'-'A']=SearchFile(HelpFile,"|KWDATA",&FileStart)&&SearchFile(HelpFile,"|KWBTREE",&FileStart);
  2438.             fprintf(hpj,"\n");
  2439.         }
  2440.         else  // else get 3.1 System records
  2441.         {
  2442.             CurrentLocation=12;
  2443.             lastpos=ftell(HelpFile);
  2444.             SWin=NULL;
  2445.             macro=0;
  2446.             fbreak=0;
  2447.             windows=0;
  2448.             groups=0;
  2449.             keyword=0;
  2450.             while(CurrentLocation<FileHdr.FileSize)
  2451.             {
  2452.                 myFRead(&SystemRec,sizeof(SYSTEMREC),HelpFile);
  2453.                 CurrentLocation+=4;
  2454.                 if(SystemRec.DataSize)
  2455.                 {
  2456.                     data=myMalloc(SystemRec.DataSize+6);
  2457.                     myFRead(data,SystemRec.DataSize,HelpFile);
  2458.                     data[SystemRec.DataSize]='\0';
  2459.                     CurrentLocation+=SystemRec.DataSize;
  2460.                     switch(SystemRec.RecordType)
  2461.                     {
  2462.                     case 0x0001:
  2463.                         if(*data) fprintf(hpj,"TITLE=%s\n",data);
  2464.                         break;
  2465.                     case 0x0002:
  2466.                         ptr=strchr(data,'\r');
  2467.                         if(ptr) strcpy(ptr,"%date");
  2468.                         if(*data) fprintf(hpj,"COPYRIGHT=%s\n",data);
  2469.                         break;
  2470.                     case 0x0003:
  2471.                         if(*(long *)data!=0L)
  2472.                         {
  2473.                             ptr=TopicName(*(long *)data,FALSE);
  2474.                             if(ptr) fprintf(hpj,"CONTENTS=%s\n",ptr);
  2475.                         }
  2476.                         break;
  2477.                     case 0x0004:
  2478.                         macro=1;
  2479.                         break;
  2480.                     case 0x0005:
  2481.                         fprintf(hpj,"ICON=%s\n",IconFileName);
  2482.                         f=myFOpen(IconFileName,"wb");
  2483.                         if(f)
  2484.                         {
  2485.                             fwrite(data,SystemRec.DataSize,1,f);
  2486.                             myFClose(f);
  2487.                         }
  2488.                         break;
  2489.                     case 0x0006:
  2490.                         windows++;
  2491.                         break;
  2492.                     case 0x0008:
  2493.                         if(*data) fprintf(hpj,"CITATION=%s\n",data);
  2494.                         break;
  2495.                     case 0x0009:
  2496.                         if(!mvp) fprintf(hpj,"LCID=0x%X 0x%X 0x%X\n",*(short *)(data+8),*(short *)data,*(short *)(data+2));
  2497.                         break;
  2498.                     case 0x000A:
  2499.                         if(!mvp&&*data) fprintf(hpj,"CNT=%s\n",data);
  2500.                         break;
  2501.                     case 0x000B:
  2502.                         if(!mvp) fprintf(hpj,"CHARSET=%d\n",*(unsigned char *)(data+1));
  2503.                         break;
  2504.                     case 0x000C:
  2505.                         if(mvp)
  2506.                         {
  2507.                             fbreak=1;
  2508.                         }
  2509.                         else
  2510.                         {
  2511.                             fprintf(hpj,"DEFFONT=%s,%d,%d\n",data+2,*(unsigned char *)data,*(unsigned char *)(data+1));
  2512.                         }
  2513.                         break;
  2514.                     case 0x000D:
  2515.                         if(mvp) groups=1;
  2516.                         break;
  2517.                     case 0x000E:
  2518.                         if(mvp)
  2519.                         {
  2520.                             keyword=1;
  2521.                         }
  2522.                         else
  2523.                         {
  2524.                             fprintf(hpj,"INDEX_SEPARATORS=\"%s\"\n",data);
  2525.                             strcpy(index_separators,data);
  2526.                         }
  2527.                         break;
  2528.                     }
  2529.                     free(data);
  2530.                 }
  2531.             }
  2532.             if(SysHeader.Version>3||SysHeader.Version==3&&SysHeader.Revision>=33)
  2533.             {
  2534.                 i=0;
  2535.                 if(lzcompressed) i|=8;
  2536.                 if(NewPhrases) i|=4; else if(PhraseCount) i|=2;
  2537.                 fprintf(hpj,"COMPRESS=%d\n",i);
  2538.             }
  2539.             else if(!lzcompressed)
  2540.             {
  2541.                 fprintf(hpj,"COMPRESS=FALSE\n");
  2542.             }
  2543.             else if(PhraseCount)
  2544.             {
  2545.                 fprintf(hpj,"COMPRESS=TRUE\n");
  2546.             }
  2547.             else
  2548.             {
  2549.                 fprintf(hpj,"COMPRESS=MEDIUM\n");
  2550.             }
  2551.             if(SysHeader.Flags==8) fprintf(hpj,"CDROMOPT=TRUE\n");
  2552.             for(i='A';i<='z';i++)
  2553.             {
  2554.                 char data[10];
  2555.                 char tree[10];
  2556.  
  2557.                 sprintf(data,"|%cWDATA",i);
  2558.                 sprintf(tree,"|%cWBTREE",i);
  2559.                 lists[i-'A']=SearchFile(HelpFile,data,&FileStart)&&SearchFile(HelpFile,tree,&FileStart);
  2560.                 if(i!='K'&&i!='A'&&i!='a'&&i!='k'&&lists[i-'A']) fprintf(hpj,"MULTIKEY=%c\n",i);
  2561.             }
  2562.             fprintf(hpj,"\n");
  2563.             if(windows)
  2564.             {
  2565.                 secondarywindownames=myMalloc((windows+1)*sizeof(char *));
  2566.                 for(i=0;i<=windows;i++) secondarywindownames[i]=NULL;
  2567.                 fseek(HelpFile,lastpos,SEEK_SET);
  2568.                 CurrentLocation=12;
  2569.                 fprintf(hpj,"[WINDOWS]\n");
  2570.                 i=0;
  2571.                 while(CurrentLocation<FileHdr.FileSize)
  2572.                 {
  2573.                     myFRead(&SystemRec,sizeof(SYSTEMREC),HelpFile);
  2574.                     data=myMalloc(SystemRec.DataSize+1);
  2575.                     myFRead(data,SystemRec.DataSize,HelpFile);
  2576.                     data[SystemRec.DataSize]='\0';
  2577.                     CurrentLocation=CurrentLocation+4+SystemRec.DataSize;
  2578.                     switch(SystemRec.RecordType)
  2579.                     {
  2580.                     case 0x0006:
  2581.                         SWin=(SECWINDOW *)data;
  2582.                         if(SWin->Flags&WSYSFLAG_NAME)
  2583.                         {
  2584.                             fprintf(hpj,"%s",SWin->Name);
  2585.                             secondarywindownames[i]=myStrDup(SWin->Name);
  2586.                         }
  2587.                         i++;
  2588.                         fprintf(hpj,"=");
  2589.                         if(SWin->Flags&WSYSFLAG_CAPTION) fprintf(hpj,"\"%s\"",SWin->Caption);
  2590.                         fprintf(hpj,",");
  2591.                         if(SWin->Flags&(WSYSFLAG_X|WSYSFLAG_Y|WSYSFLAG_WIDTH|WSYSFLAG_HEIGHT))
  2592.                         {
  2593.                             fprintf(hpj,"(");
  2594.                             if(SWin->Flags&WSYSFLAG_X) fprintf(hpj,"%d",SWin->X);
  2595.                             fprintf(hpj,",");
  2596.                             if(SWin->Flags&WSYSFLAG_Y) fprintf(hpj,"%d",SWin->Y);
  2597.                             fprintf(hpj,",");
  2598.                             if(SWin->Flags&WSYSFLAG_WIDTH) fprintf(hpj,"%d",SWin->Width);
  2599.                             fprintf(hpj,",");
  2600.                             if(SWin->Flags&WSYSFLAG_HEIGHT) fprintf(hpj,"%d",SWin->Height);
  2601.                             fprintf(hpj,")");
  2602.                         }
  2603.                         fprintf(hpj,",");
  2604.                         if(SWin->Maximize) fprintf(hpj,"%d",SWin->Maximize);
  2605.                         fprintf(hpj,",");
  2606.                         if(SWin->Flags&WSYSFLAG_RGB) fprintf(hpj,"(%d,%d,%d)",SWin->Rgb[0],SWin->Rgb[1],SWin->Rgb[2]);
  2607.                         fprintf(hpj,",");
  2608.                         if(SWin->Flags&WSYSFLAG_RGBNSR) fprintf(hpj,"(%d,%d,%d)",SWin->RgbNsr[0],SWin->RgbNsr[1],SWin->RgbNsr[2]);
  2609.                         if(SWin->Flags&(WSYSFLAG_TOP|WSYSFLAG_AUTOSIZEHEIGHT))
  2610.                         {
  2611.                             if(SWin->Flags&WSYSFLAG_AUTOSIZEHEIGHT)
  2612.                             {
  2613.                                 if(SWin->Flags&WSYSFLAG_TOP)
  2614.                                 {
  2615.                                     fprintf(hpj,",f3");
  2616.                                 }
  2617.                                 else
  2618.                                 {
  2619.                                     fprintf(hpj,",f2");
  2620.                                 }
  2621.                             }
  2622.                             else fprintf(hpj,",1");
  2623.                         }
  2624.                         fprintf(hpj,"\n");
  2625.                         break;
  2626.                     }
  2627.                     free(data);
  2628.                 }
  2629.                 fprintf(hpj,"\n");
  2630.             }
  2631.             if(macro)
  2632.             {
  2633.                 fseek(HelpFile,lastpos,SEEK_SET);
  2634.                 CurrentLocation=12;
  2635.                 fprintf(hpj,"[CONFIG]\n");
  2636.                 while(CurrentLocation<FileHdr.FileSize)
  2637.                 {
  2638.                     myFRead(&SystemRec,sizeof(SYSTEMREC),HelpFile);
  2639.                     data=myMalloc(SystemRec.DataSize+1);
  2640.                     myFRead(data,SystemRec.DataSize,HelpFile);
  2641.                     data[SystemRec.DataSize]='\0';
  2642.                     CurrentLocation=CurrentLocation+4+SystemRec.DataSize;
  2643.                     switch(SystemRec.RecordType)
  2644.                     {
  2645.                     case 0x0004:
  2646.                         if(sscanf(data,"SPC(%ld)%n",&color,&n)>0)
  2647.                         {
  2648.                             fprintf(hpj,"SPC(%u,%u,%u)%s\n",(unsigned char)(color),(unsigned char)(color>>8),(unsigned char)(color>>16),data+n);
  2649.                         }
  2650.                         else
  2651.                         {
  2652.                             fprintf(hpj,"%s\n",data);
  2653.                         }
  2654.                         break;
  2655.                     }
  2656.                     free(data);
  2657.                 }
  2658.                 fprintf(hpj,"\n");
  2659.             }
  2660.             if(fbreak)
  2661.             {
  2662.                 fseek(HelpFile,lastpos,SEEK_SET);
  2663.                 CurrentLocation=12;
  2664.                 fprintf(hpj,"[FTINDEX]\n");
  2665.                 while(CurrentLocation<FileHdr.FileSize)
  2666.                 {
  2667.                     myFRead(&SystemRec,sizeof(SYSTEMREC),HelpFile);
  2668.                     data=myMalloc(SystemRec.DataSize+1);
  2669.                     myFRead(data,SystemRec.DataSize,HelpFile);
  2670.                     data[SystemRec.DataSize]='\0';
  2671.                     CurrentLocation=CurrentLocation+4+SystemRec.DataSize;
  2672.                     switch(SystemRec.RecordType)
  2673.                     {
  2674.                     case 0x000C:
  2675.                         switch(sscanf(data,"%d%s%s%s%s",&dtype,buffer,buffer+256,buffer+512,buffer+1024))
  2676.                         {
  2677.                         case 5:
  2678.                             fprintf(hpj,"dtype%d=%s!%s,%s,%s\n",dtype,buffer,buffer+256,buffer+512,buffer+1024);
  2679.                             break;
  2680.                         case 4:
  2681.                             fprintf(hpj,"dtype%d=%s!%s,%s\n",dtype,buffer,buffer+256,buffer+512);
  2682.                             break;
  2683.                         case 3:
  2684.                             fprintf(hpj,"dtype%d=%s!%s\n",dtype,buffer,buffer+256);
  2685.                             break;
  2686.                         default:
  2687.                             fprintf(stderr,"Unknown format in '%s'\n",data);
  2688.                         }
  2689.                         break;
  2690.                     }
  2691.                     free(data);
  2692.                 }
  2693.                 fprintf(hpj,"\n");
  2694.             }
  2695.             if(groups)
  2696.             {
  2697.                 fseek(HelpFile,lastpos,SEEK_SET);
  2698.                 CurrentLocation=12;
  2699.                 fprintf(hpj,"[GROUPS]\n");
  2700.                 while(CurrentLocation<FileHdr.FileSize)
  2701.                 {
  2702.                     myFRead(&SystemRec,sizeof(SYSTEMREC),HelpFile);
  2703.                     data=myMalloc(SystemRec.DataSize+1);
  2704.                     myFRead(data,SystemRec.DataSize,HelpFile);
  2705.                     data[SystemRec.DataSize]='\0';
  2706.                     CurrentLocation=CurrentLocation+4+SystemRec.DataSize;
  2707.                     switch(SystemRec.RecordType)
  2708.                     {
  2709.                     case 0x000D:
  2710.                         groups=strcspn(data,".");
  2711.                         if(strcmp(data+groups,".GRP \"\" ")==0)
  2712.                         {
  2713.                             data[groups]='\0';
  2714.                             fprintf(hpj,"group=%s\n",data);
  2715.                         }
  2716.                         else if(memcmp(data+groups,".GRP ",5)==0)
  2717.                         {
  2718.                             data[groups]='\0';
  2719.                             fprintf(hpj,"group=%s,%s\n",data,data+groups+5);
  2720.                         }
  2721.                         break;
  2722.                     }
  2723.                     free(data);
  2724.                 }
  2725.                 fprintf(hpj,"\n");
  2726.             }
  2727.             if(keyword)
  2728.             {
  2729.                 fseek(HelpFile,lastpos,SEEK_SET);
  2730.                 CurrentLocation=12;
  2731.                 fprintf(hpj,"[KEYINDEX]\n");
  2732.                 while(CurrentLocation<FileHdr.FileSize)
  2733.                 {
  2734.                     myFRead(&SystemRec,sizeof(SYSTEMREC),HelpFile);
  2735.                     data=myMalloc(SystemRec.DataSize+1);
  2736.                     myFRead(data,SystemRec.DataSize,HelpFile);
  2737.                     data[SystemRec.DataSize]='\0';
  2738.                     CurrentLocation=CurrentLocation+4+SystemRec.DataSize;
  2739.                     switch(SystemRec.RecordType)
  2740.                     {
  2741.                     case 0x000E:
  2742.                         fprintf(hpj,"keyword=%c,\"%s\"\n",data[1],data+30);
  2743.                         keyindex[data[1]-'A']=TRUE;
  2744.                         break;
  2745.                     }
  2746.                     free(data);
  2747.                 }
  2748.                 fprintf(hpj,"\n");
  2749.             }
  2750.             for(i=0;i<windows;i++)
  2751.             {
  2752.                 sprintf(HelpFileTitle,"|CF%d",i);
  2753.                 if(SearchFile(HelpFile,HelpFileTitle,&FileStart))
  2754.                 {
  2755.                     fprintf(hpj,"[CONFIG:%d]\n",i);
  2756.                     fseek(HelpFile,FileStart,SEEK_SET);
  2757.                     myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  2758.                     for(macro=0;macro<FileHdr.FileSize;macro+=strlen(buffer)+1)
  2759.                     {
  2760.                         StringRead(buffer,sizeof(buffer),HelpFile);
  2761.                         fprintf(hpj,"%s\n",buffer);
  2762.                     }
  2763.                     fprintf(hpj,"\n");
  2764.                 }
  2765.             }
  2766.         }
  2767.     }
  2768. }
  2769.  
  2770. char *PrintPhrase(unsigned int PhraseNum,char *out)
  2771. {
  2772.     char *ptr;
  2773.     char *end;
  2774.  
  2775.     if(PhraseNum>PhraseCount)
  2776.     {
  2777.         error("phrase %u does not exist %u\n",PhraseNum,PhraseCount);
  2778.         return out;
  2779.     }
  2780.     if(NewPhrases)
  2781.     {
  2782.         ptr=NewPhrases+Offsets[PhraseNum];
  2783.         end=NewPhrases+Offsets[PhraseNum+1];
  2784.     }
  2785.     else
  2786.     {
  2787.         ptr=(char *)Offsets+Offsets[PhraseNum];
  2788.         end=(char *)Offsets+Offsets[PhraseNum+1];
  2789.     }
  2790.     while(ptr<end)
  2791.     {
  2792.         if(out)
  2793.         {
  2794.             *out++=*ptr++;
  2795.         }
  2796.         else if(isprint((unsigned char)*ptr))
  2797.         {
  2798.             putchar(*ptr++);
  2799.         }
  2800.         else
  2801.         {
  2802.             printf("(%02x)",*(unsigned char *)ptr++);
  2803.         }
  2804.     }
  2805.     if(out) *out='\0';
  2806.     return out;
  2807. }
  2808.  
  2809. BOOL GetBit(FILE *f)
  2810. {
  2811.     static unsigned long mask;
  2812.     static unsigned long value;
  2813.  
  2814.     if(f)
  2815.     {
  2816.         mask<<=1;
  2817.         if(!mask)
  2818.         {
  2819.             value=GetDWord((MFILE *)f);
  2820.             mask=1L;
  2821.         }
  2822.     }
  2823.     else
  2824.     {
  2825.         mask=0L; // initialize
  2826.     }
  2827.     return (value&mask)!=0L;
  2828. }
  2829.  
  2830. BOOL PhraseLoad(FILE *HelpFile)
  2831. {
  2832.     FILEHEADER FileHdr;
  2833.     char junk[30];
  2834.     BOOL newphrases;
  2835.     PHRINDEXHDR PhrIndexHdr;
  2836.     unsigned int n;
  2837.     long l,offset;
  2838.     char *ptr;
  2839.     long FileStart,SavePos;
  2840.  
  2841.     if(SearchFile(HelpFile,"|Phrases",&FileStart))
  2842.     {
  2843.         fseek(HelpFile,FileStart,SEEK_SET);
  2844.         myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  2845.         offset=ftell(HelpFile);
  2846.         PhraseCount=getw(HelpFile);
  2847.         newphrases=PhraseCount==0x0800;
  2848.         if(newphrases) PhraseCount=getw(HelpFile);
  2849.         if(getw(HelpFile)!=0x0100)
  2850.         {
  2851.             fprintf(stderr,"Phrases file structure unknown\n");
  2852.             return FALSE;
  2853.         }
  2854.         if(PhraseCount)
  2855.         {
  2856.             if(before31)
  2857.             {
  2858.                 Offsets=myMalloc(FileHdr.FileSize-4);
  2859.                 myFRead(Offsets,FileHdr.FileSize-4,HelpFile);
  2860.             }
  2861.             else
  2862.             {
  2863.                 myFRead(&l,sizeof(l),HelpFile);
  2864.                 if(newphrases) myFRead(&junk,sizeof(junk),HelpFile);
  2865.                 Offsets=myMalloc(2*(PhraseCount+1)+l);
  2866.                 ptr=(char *)(Offsets+PhraseCount+1);
  2867.                 myFRead(Offsets,2*(PhraseCount+1),HelpFile);
  2868.                 n=LzExpand((MFILE *)HelpFile,FileHdr.FileSize-(ftell(HelpFile)-offset),NULL,ptr);
  2869.                 if(n!=l)
  2870.                 {
  2871.                     error("Phrases decompressed into %u instead %ld\n",n,l);
  2872.                 }
  2873.             }
  2874.             printf("%u phrases loaded\n",PhraseCount);
  2875.         }
  2876.         return TRUE;
  2877.     }
  2878.     else if(SearchFile(HelpFile,"|PhrIndex",&FileStart))
  2879.     {
  2880.         fseek(HelpFile,FileStart,SEEK_SET);
  2881.         myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  2882.         myFRead(&PhrIndexHdr,sizeof(PhrIndexHdr),HelpFile);
  2883.         if(PhrIndexHdr.always4A01!=1&&PhrIndexHdr.always4A01!=0x4A01) fprintf(stderr,"PhrIndexHdr.always4A01=%04x\n",PhrIndexHdr.always4A01);
  2884.         if(PhrIndexHdr.always0!=0) fprintf(stderr,"PhrIndexHdr.always0=%04x\n",PhrIndexHdr.always0);
  2885.         if(PhrIndexHdr.always4A00!=0x4A00&&PhrIndexHdr.always4A00!=0x4A01&&PhrIndexHdr.always4A00!=0x4A02) fprintf(stderr,"PhrIndexHdr.always4A00=%04x\n",PhrIndexHdr.always4A00);
  2886.         SavePos=ftell(HelpFile);
  2887.         if(SearchFile(HelpFile,"|PhrImage",&FileStart))
  2888.         {
  2889.             fseek(HelpFile,FileStart,SEEK_SET);
  2890.             myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  2891.             if(FileHdr.FileSize!=PhrIndexHdr.phrimagecompressedsize)
  2892.             {
  2893.                 fprintf(stderr,"PhrImage FileSize %ld, in PhrIndex.FileHdr %ld\n",PhrIndexHdr.phrimagecompressedsize,FileHdr.FileSize);
  2894.             }
  2895.             PhraseCount=(unsigned int)PhrIndexHdr.entries;
  2896.             Offsets=myMalloc(2*(PhraseCount+1));
  2897.             NewPhrases=myMalloc(PhrIndexHdr.phrimagesize);
  2898.             if(PhrIndexHdr.phrimagesize==PhrIndexHdr.phrimagecompressedsize)
  2899.             {
  2900.                 myFRead(NewPhrases,PhrIndexHdr.phrimagesize,HelpFile);
  2901.             }
  2902.             else
  2903.             {
  2904.                 n=LzExpand((MFILE *)HelpFile,FileHdr.FileSize,NULL,NewPhrases);
  2905.                 if(n!=PhrIndexHdr.phrimagesize)
  2906.                 {
  2907.                     fprintf(stderr,"PhrImage Size %ld, in PhrIndex %u\n",PhrIndexHdr.phrimagesize,n);
  2908.                 }
  2909.             }
  2910.             fseek(HelpFile,SavePos,SEEK_SET);
  2911.             GetBit(NULL);
  2912.             offset=0;
  2913.             Offsets[0]=offset;
  2914.             for(l=0;l<PhrIndexHdr.entries;l++)
  2915.             {
  2916.                 for(n=1;GetBit(HelpFile);n+=1<<PhrIndexHdr.bits) ;
  2917.                 if(GetBit(HelpFile)) n+=1;
  2918.                 if(PhrIndexHdr.bits>1) if(GetBit(HelpFile)) n+=2;
  2919.                 if(PhrIndexHdr.bits>2) if(GetBit(HelpFile)) n+=4;
  2920.                 if(PhrIndexHdr.bits>3) if(GetBit(HelpFile)) n+=8;
  2921.                 if(PhrIndexHdr.bits>4) if(GetBit(HelpFile)) n+=16;
  2922.                 offset+=n;
  2923.                 Offsets[(int)l+1]=offset;
  2924.             }
  2925.         }
  2926.         printf("%u phrases loaded\n",PhraseCount);
  2927.         return TRUE;
  2928.     }
  2929.     return FALSE;
  2930. }
  2931.  
  2932. void PhraseDump(void)
  2933. {
  2934.     unsigned int n;
  2935.  
  2936.     for(n=0;n<PhraseCount;n++)
  2937.     {
  2938.         printf("%-5d - ",n);
  2939.         PrintPhrase(n,NULL);
  2940.         printf("\n");
  2941.     }
  2942. }
  2943.  
  2944. void PhraseList(char *FileName)
  2945. {
  2946.     FILE *f;
  2947.     unsigned int n;
  2948.     static char buffer[512]; // max. length of a phrase 512 char
  2949.  
  2950.     if(PhraseCount)
  2951.     {
  2952.         f=myFOpen(FileName,"wt");
  2953.         if(f)
  2954.         {
  2955.             for(n=0;n<PhraseCount;n++)
  2956.             {
  2957.                 PrintPhrase(n,buffer);
  2958.                 if(strlen(buffer)>=sizeof(buffer))
  2959.                 {
  2960.                     error("Buffer overflow\n");
  2961.                 }
  2962.                 fprintf(f,"%s\n",buffer);
  2963.             }
  2964.             myFClose(f);
  2965.         }
  2966.     }
  2967. }
  2968.  
  2969. void FontDump(FILE *HelpFile,long FileStart)
  2970. {
  2971.     FILEHEADER FileHdr;
  2972.     FONTHEADER FontHdr;
  2973.     NEWFONT NewFont;
  2974.     FONTDESCRIPTOR FontDesc;
  2975.     int i,n;
  2976.     char FontName[80];
  2977.  
  2978.     // Go to the FONT file and get the headers
  2979.     fseek(HelpFile,FileStart,SEEK_SET);
  2980.     myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  2981.     FileStart=ftell(HelpFile);
  2982.     myFRead(&FontHdr,sizeof(FontHdr),HelpFile);
  2983.     printf("|FONTS\n\n Number Fonts: %d\n",FontHdr.NumFacenames);
  2984.     printf("Font # - Font Name\n");
  2985.     n=(FontHdr.DescriptorsOffset-FontHdr.FacenamesOffset)/FontHdr.NumFacenames;
  2986.     for(i=0;i<FontHdr.NumFacenames;i++)
  2987.     {
  2988.         fseek(HelpFile,FileStart+FontHdr.FacenamesOffset+n*i,SEEK_SET);
  2989.         StringRead(FontName,sizeof(FontName),HelpFile);
  2990.         printf(" %3d   - %.20s\n",i,FontName);
  2991.     }
  2992.     printf("Num Font Descriptors: %d\n\n",FontHdr.NumDescriptors);
  2993.     printf("Attributes: n=none  b=bold  i=ital  u=undr  s=strkout  d=dblundr  C=smallcaps\n\n");
  2994.     printf("Font Name            PointSize  Family   FG RGB      BG RGB     Attributes\n");
  2995.     printf("--------------------------------------------------------------------------\n");
  2996.     if(FontHdr.FacenamesOffset>=12)
  2997.     {
  2998.         for(i=0;i<FontHdr.NumDescriptors;i++)
  2999.         {
  3000.             fseek(HelpFile,FileStart+FontHdr.DescriptorsOffset+sizeof(NewFont)*i,SEEK_SET);
  3001.             myFRead(&NewFont,sizeof(NewFont),HelpFile);
  3002.             fseek(HelpFile,FileStart+FontHdr.FacenamesOffset+n*NewFont.FontName,SEEK_SET);
  3003.             StringRead(FontName,sizeof(FontName),HelpFile);
  3004.             printf("%02x %-20.20s %5ld %5d ",NewFont.unknown1,FontName,NewFont.Height,NewFont.Weight);
  3005.             printf("0x%02x%02x%02x ",NewFont.FGRGB[2],NewFont.FGRGB[1],NewFont.FGRGB[0]);
  3006.             printf("0x%02x%02x%02x ",NewFont.BGRGB[2],NewFont.BGRGB[1],NewFont.BGRGB[0]);
  3007.             if(NewFont.Italic) putchar('i');
  3008.             if(NewFont.Underline) putchar('u');
  3009.             if(NewFont.StrikeOut) putchar('s');
  3010.             if(NewFont.DoubleUnderline) putchar('d');
  3011.             if(NewFont.SmallCaps) putchar('C');
  3012.             printf("\n");
  3013.         }
  3014.     }
  3015.     else
  3016.     {
  3017.         for(i=0;i<FontHdr.NumDescriptors;i++)
  3018.         {
  3019.             fseek(HelpFile,FileStart+FontHdr.DescriptorsOffset+sizeof(FontDesc)*i,SEEK_SET);
  3020.             myFRead(&FontDesc,sizeof(FontDesc),HelpFile);
  3021.             fseek(HelpFile,FileStart+FontHdr.FacenamesOffset+n*FontDesc.FontName,SEEK_SET);
  3022.             StringRead(FontName,sizeof(FontName),HelpFile);
  3023.             printf("%-20.20s    %4.1f    ",FontName,(float)(FontDesc.HalfPoints / 2));
  3024.             switch(FontDesc.FontFamily)
  3025.             {
  3026.             case FAM_MODERN:
  3027.                 printf("Modern");
  3028.                 break;
  3029.             case FAM_ROMAN:
  3030.                 printf("Roman ");
  3031.                 break;
  3032.             case FAM_SWISS:
  3033.                 printf("Swiss ");
  3034.                 break;
  3035.             case FAM_SCRIPT:
  3036.                 printf("Script");
  3037.                 break;
  3038.             case FAM_DECOR:
  3039.                 printf("Decor ");
  3040.                 break;
  3041.             default:
  3042.                 printf("0X%02X ",FontDesc.FontFamily);
  3043.                 break;
  3044.             }
  3045.             printf("0x%02x%02x%02xX ",FontDesc.FGRGB[2],FontDesc.FGRGB[1],FontDesc.FGRGB[0]);
  3046.             printf("0x%02x%02x%02xX ",FontDesc.BGRGB[2],FontDesc.BGRGB[1],FontDesc.BGRGB[0]);
  3047.             if(FontDesc.Attributes==0) putchar('n');
  3048.             if(FontDesc.Attributes&FONT_BOLD) putchar('b');
  3049.             if(FontDesc.Attributes&FONT_ITAL) putchar('i');
  3050.             if(FontDesc.Attributes&FONT_UNDR) putchar('u');
  3051.             if(FontDesc.Attributes&FONT_STRK) putchar('s');
  3052.             if(FontDesc.Attributes&FONT_DBUN) putchar('d');
  3053.             if(FontDesc.Attributes&FONT_SMCP) putchar('C');
  3054.             printf("\n");
  3055.         }
  3056.     }
  3057.     if(FontHdr.FacenamesOffset>=12) for(i=0;i<FontHdr.NumFormats;i++)
  3058.     {
  3059.         fseek(HelpFile,FileStart+FontHdr.FormatsOffset+0x91*i,SEEK_SET);
  3060.         myFRead(buffer,0x91,HelpFile);
  3061.         for(n=0;n<0x50;n++)
  3062.         {
  3063.             if(n%16==0) printf("%04x:",n);
  3064.             printf(" %02x",(unsigned char)buffer[n]);
  3065.             if(n%16==15) printf("\n");
  3066.         }
  3067.         printf("%s\n",buffer+0x50);
  3068.     }
  3069. }
  3070.  
  3071. void FontLoad(FILE *HelpFile,FILE *rtf)
  3072. {
  3073.     static char *familyname[]={"swiss","modern","roman","swiss","script","decor"};
  3074.     FILEHEADER FileHdr;
  3075.     FONTHEADER FontHdr;
  3076.     char FontName[80];
  3077.     long FileStart;
  3078.     long FontStart;
  3079.     int i,n;
  3080.     struct { unsigned char r,g,b; } color[128];
  3081.     unsigned char *family;
  3082.     int colors;
  3083.  
  3084.     if(SearchFile(HelpFile,"|FONT",&FileStart))
  3085.     {
  3086.         fseek(HelpFile,FileStart,SEEK_SET);
  3087.         myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  3088.         FontStart=ftell(HelpFile);
  3089.         myFRead(&FontHdr,sizeof(FontHdr),HelpFile);
  3090.         fontnames=FontHdr.NumFacenames;
  3091.         n=(FontHdr.DescriptorsOffset-FontHdr.FacenamesOffset)/fontnames;
  3092.         fontname=myMalloc(fontnames*sizeof(char *));
  3093.         for(i=0;i<fontnames;i++)
  3094.         {
  3095.             fseek(HelpFile,FontStart+FontHdr.FacenamesOffset+n*i,SEEK_SET);
  3096.             StringRead(FontName,sizeof(FontName),HelpFile);
  3097.             fontname[i]=myStrDup(FontName);
  3098.         }
  3099.         fseek(HelpFile,FontStart+FontHdr.DescriptorsOffset,SEEK_SET);
  3100.         if(FontHdr.FacenamesOffset>=12)
  3101.         {
  3102.             newfonts=FontHdr.NumDescriptors;
  3103.             newfont=myMalloc(newfonts*sizeof(NEWFONT));
  3104.             myFRead(newfont,sizeof(NEWFONT)*newfonts,HelpFile);
  3105.             family=myMalloc(fontnames*sizeof(unsigned char));
  3106.             for(i=0;i<fontnames;i++) family[i]=0;
  3107.             colors=1;     // auto
  3108.             color[0].r=1;
  3109.             color[0].g=1;
  3110.             color[0].b=0;
  3111.             scaling=1;
  3112.             for(i=0;i<FontHdr.NumDescriptors;i++)
  3113.             {
  3114.                 family[newfont[i].FontName]=newfont[i].PitchAndFamily>>4;
  3115.                 for(n=0;n<colors;n++)
  3116.                 {
  3117.                     if(newfont[i].FGRGB[0]==color[n].r&&newfont[i].FGRGB[1]==color[n].g&&newfont[i].FGRGB[2]==color[n].b) break;
  3118.                 }
  3119.                 if(n==colors)
  3120.                 {
  3121.                     color[colors].r=newfont[i].FGRGB[0];
  3122.                     color[colors].g=newfont[i].FGRGB[1];
  3123.                     color[colors].b=newfont[i].FGRGB[2];
  3124.                     colors++;
  3125.                 }
  3126.                 newfont[i].FGRGB[0]=n;
  3127.             }
  3128.         }
  3129.         else
  3130.         {
  3131.             fonts=FontHdr.NumDescriptors;
  3132.             font=myMalloc(fonts*sizeof(FONTDESCRIPTOR));
  3133.             myFRead(font,sizeof(FONTDESCRIPTOR)*fonts,HelpFile);
  3134.             family=myMalloc(fontnames*sizeof(unsigned char));
  3135.             for(i=0;i<fontnames;i++) family[i]=0;
  3136.             colors=1;     // auto
  3137.             color[0].r=1;
  3138.             color[0].g=1;
  3139.             color[0].b=0;
  3140.             for(i=0;i<FontHdr.NumDescriptors;i++)
  3141.             {
  3142.                 family[font[i].FontName]=font[i].FontFamily;
  3143.                 for(n=0;n<colors;n++)
  3144.                 {
  3145.                     if(font[i].FGRGB[0]==color[n].r&&font[i].FGRGB[1]==color[n].g&&font[i].FGRGB[2]==color[n].b) break;
  3146.                 }
  3147.                 if(n==colors)
  3148.                 {
  3149.                     color[colors].r=font[i].FGRGB[0];
  3150.                     color[colors].g=font[i].FGRGB[1];
  3151.                     color[colors].b=font[i].FGRGB[2];
  3152.                     colors++;
  3153.                 }
  3154.                 font[i].FGRGB[0]=n;
  3155.             }
  3156.         }
  3157.         if(rtf)
  3158.         {
  3159.             fprintf(rtf,"{\\fonttbl");
  3160.             for(n=0;n<fontnames;n++) fprintf(rtf,"{\\f%d\\f%s %s;}",n,familyname[family[n]],fontname[n]);
  3161.             fprintf(rtf,"}\n");
  3162.             if(colors>1)
  3163.             {
  3164.                 fprintf(rtf,"{\\colortbl;");
  3165.                 for(n=1;n<colors;n++) fprintf(rtf,"\\red%d\\green%d\\blue%d;",color[n].r,color[n].g,color[n].b);
  3166.                 fprintf(rtf,"}\n");
  3167.             }
  3168.         }
  3169.         printf("%u font names, %u font descriptors loaded\n",fontnames,FontHdr.NumDescriptors);
  3170.     }
  3171. }
  3172.  
  3173. void ToMapLoad(FILE *HelpFile)
  3174. {
  3175.     FILEHEADER FileHdr;
  3176.     long FileStart;
  3177.  
  3178.     if(SearchFile(HelpFile,"|TOMAP",&FileStart))
  3179.     {
  3180.         fseek(HelpFile,FileStart,SEEK_SET);
  3181.         myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  3182.         if(FileHdr.FileSize!=(size_t)FileHdr.FileSize)
  3183.         {
  3184.             error("Too many Topics in |TOMAP\n");
  3185.             exit(1);
  3186.         }
  3187.         Topics=(int)(FileHdr.FileSize/sizeof(long));
  3188.         Topic=myMalloc(FileHdr.FileSize);
  3189.         myFRead(Topic,FileHdr.FileSize,HelpFile);
  3190.     }
  3191. }
  3192.  
  3193. void ToMapDump(FILE *HelpFile,long FileStart)
  3194. {
  3195.     FILEHEADER FileHdr;
  3196.     long TopicOffset,i;
  3197.  
  3198.     fseek(HelpFile,FileStart,SEEK_SET);
  3199.     myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  3200.     printf("Topic # - Topic Start\n");
  3201.     for(i=0;i*4L<FileHdr.FileSize;i++)
  3202.     {
  3203.         myFRead(&TopicOffset,sizeof(TopicOffset),HelpFile);
  3204.         printf("%-12ld - 0x%08lX\n",i,TopicOffset);
  3205.     }
  3206. }
  3207.  
  3208. long TopicRead(FILE *HelpFile,long TopicAddress,void *dest,long NumBytes)
  3209. {
  3210.     static FILEHEADER FileHdr;
  3211.     static long NextTopic;
  3212.     static long BlockNum;
  3213.     long NewBlockNum;
  3214.     unsigned char *Dest;
  3215.     unsigned int BytesToRead;
  3216.     static unsigned int DecompSize;
  3217.     static unsigned int offset;
  3218.     TOPICLINK *TopicLink;
  3219.     unsigned int BytesRead;
  3220.     static long BytesInBlock;
  3221.     long here;
  3222.  
  3223.     Dest=dest;
  3224.     if(TopicAddress)
  3225.     {
  3226.         if(!TopicFileStart)
  3227.         {
  3228.             myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  3229.             TopicFileStart=ftell(HelpFile);
  3230.             NextTopic=12L;
  3231.             BlockNum=-1L;
  3232.         }
  3233.         NewBlockNum=TopicAddress/DecompressSize;
  3234.         if(BlockNum!=NewBlockNum)
  3235.         {
  3236.             if(NewBlockNum*TopicBlockSize>=FileHdr.FileSize) return 0;
  3237.             BlockNum=NewBlockNum;
  3238.             TopicOffset=BlockNum*0x8000L;
  3239.             dontCount=TRUE;
  3240.             fseek(HelpFile,TopicFileStart+TopicBlockSize*NewBlockNum,SEEK_SET);
  3241.             BytesToRead=TopicBlockSize;
  3242.             if(FileHdr.FileSize-TopicBlockSize*NewBlockNum<BytesToRead)
  3243.             {
  3244.                 BytesToRead=(unsigned int)(FileHdr.FileSize-TopicBlockSize*NewBlockNum);
  3245.             }
  3246.             myFRead(buffer,sizeof(TOPICBLOCKHEADER),HelpFile);
  3247.             BytesToRead-=sizeof(TOPICBLOCKHEADER);
  3248.             if(lzcompressed)
  3249.             {
  3250.                 DecompSize=LzExpand((MFILE *)HelpFile,BytesToRead,NULL,buffer+sizeof(TOPICBLOCKHEADER));
  3251.             }
  3252.             else
  3253.             {
  3254.                 DecompSize=myFRead(buffer+sizeof(TOPICBLOCKHEADER),BytesToRead,HelpFile);
  3255.             }
  3256.             DecompSize+=sizeof(TOPICBLOCKHEADER);
  3257.         }
  3258.         offset=(unsigned int)(TopicAddress%DecompressSize);
  3259.         if(offset>=DecompSize) return 0L;
  3260.         TopicLink=(TOPICLINK *)(buffer+offset);
  3261.         if(before31)
  3262.         {
  3263.             NextTopic=TopicLink->NextBlock+TopicAddress;
  3264.         }
  3265.         else
  3266.         {
  3267.             NextTopic=TopicLink->NextBlock;
  3268.         }
  3269.         BytesInBlock=TopicLink->BlockSize;
  3270.     }
  3271.     here=BlockNum*DecompressSize+offset;
  3272.     for(BytesRead=0;BytesRead<NumBytes;BytesRead++)
  3273.     {
  3274.         if(BytesInBlock<=0)
  3275.         {
  3276.             return TopicRead(HelpFile,NextTopic,Dest,NumBytes-BytesRead)+BytesRead;
  3277.         }
  3278.         if(offset>=DecompSize)
  3279.         {
  3280.             NewBlockNum=BlockNum+1;
  3281.             if(NewBlockNum*TopicBlockSize>=FileHdr.FileSize) break;
  3282.             fseek(HelpFile,TopicFileStart+TopicBlockSize*NewBlockNum,SEEK_SET);
  3283.             BlockNum=NewBlockNum;
  3284.             TopicOffset=BlockNum*0x8000L;
  3285.             dontCount=TRUE;
  3286.             BytesToRead=TopicBlockSize;
  3287.             if(FileHdr.FileSize-TopicBlockSize*NewBlockNum<BytesToRead)
  3288.             {
  3289.                 BytesToRead=(unsigned int)(FileHdr.FileSize-TopicBlockSize*NewBlockNum);
  3290.             }
  3291.             myFRead(buffer,sizeof(TOPICBLOCKHEADER),HelpFile);
  3292.             BytesToRead-=sizeof(TOPICBLOCKHEADER);
  3293.             if(lzcompressed)
  3294.             {
  3295.                 DecompSize=LzExpand((MFILE *)HelpFile,BytesToRead,NULL,buffer+sizeof(TOPICBLOCKHEADER));
  3296.             }
  3297.             else
  3298.             {
  3299.                 DecompSize=myFRead(buffer+sizeof(TOPICBLOCKHEADER),BytesToRead,HelpFile);
  3300.             }
  3301.             DecompSize+=sizeof(TOPICBLOCKHEADER);
  3302.             offset=sizeof(TOPICBLOCKHEADER);
  3303.         }
  3304.         *Dest++=buffer[offset++];
  3305.         BytesInBlock--;
  3306.     }
  3307.     TopicPos=here;
  3308.     return BytesRead;
  3309. }
  3310.  
  3311. #ifndef __TURBOC__
  3312. void textcolor(int color) { }
  3313. #endif
  3314.  
  3315. void ListTopic(FILE *HelpFile)
  3316. {
  3317.     long FileStart,BlockNum,BufferSize;
  3318.     unsigned int BytesToRead,DecompSize;
  3319.     FILEHEADER FileHdr;
  3320.     TOPICLINK *TopicLink;
  3321.     long NextTopicLink,NextLinkData1,NextLinkData2,NextBlockEnd;
  3322.     int i,j,n,color;
  3323.  
  3324.     if(SearchFile(HelpFile,"|TOPIC",&FileStart))
  3325.     {
  3326.         if(before31)
  3327.         {
  3328.             BufferSize=TopicBlockSize;
  3329.         }
  3330.         else
  3331.         {
  3332.             BufferSize=0x4000;
  3333.         }
  3334.         NextTopicLink=12;
  3335.         fseek(HelpFile,FileStart,SEEK_SET);
  3336.         myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  3337.         printf("|TOPIC FileSize: 0x%08lX, TopicBlockSize=%u\n",FileHdr.FileSize,TopicBlockSize);
  3338.         textcolor(4);
  3339.         cprintf("TOPICBLOCKHEADER ");
  3340.         textcolor(2);
  3341.         cprintf("TOPICLINK ");
  3342.         textcolor(5);
  3343.         cprintf("LinkData1 ");
  3344.         textcolor(3);
  3345.         cprintf("LinkData2 ");
  3346.         textcolor(1);
  3347.         cprintf("unused\r\n");
  3348.         textcolor(7);
  3349.         color=2;
  3350.         NextTopicLink=12;
  3351.         for(BlockNum=0;BlockNum*TopicBlockSize<FileHdr.FileSize;BlockNum++)
  3352.         {
  3353.             BytesToRead=TopicBlockSize;
  3354.             if(FileHdr.FileSize-TopicBlockSize*BlockNum<BytesToRead)
  3355.             {
  3356.                 BytesToRead=(unsigned int)(FileHdr.FileSize-TopicBlockSize*BlockNum);
  3357.             }
  3358.             myFRead(buffer,sizeof(TOPICBLOCKHEADER),HelpFile);
  3359.             cprintf("----------------------------------------------------------------------------\r\n");
  3360.             BytesToRead-=sizeof(TOPICBLOCKHEADER);
  3361.             if(lzcompressed)
  3362.             {
  3363.                 DecompSize=LzExpand((MFILE *)HelpFile,BytesToRead,NULL,buffer+sizeof(TOPICBLOCKHEADER));
  3364.             }
  3365.             else
  3366.             {
  3367.                 DecompSize=myFRead(buffer+sizeof(TOPICBLOCKHEADER),BytesToRead,HelpFile);
  3368.             }
  3369.             DecompSize+=sizeof(TOPICBLOCKHEADER);
  3370.             if(((long *)buffer)[1]>BufferSize*BlockNum) NextTopicLink=((long *)buffer)[1]%BufferSize;
  3371.             for(i=0;i<DecompSize;i+=16)
  3372.             {
  3373.                 cprintf("0x%08lX: ",BufferSize*BlockNum+i);
  3374.                 textcolor(color);
  3375.                 n=(int)(DecompSize-i>16?16:DecompSize-i);
  3376.                 for(j=0;j<n;j++)
  3377.                 {
  3378.                     if(i+j==0) textcolor(4);
  3379.                     if(i+j==12) textcolor(color);
  3380.                     if(i+j==NextTopicLink)
  3381.                     {
  3382.                         textcolor(color=2);
  3383.                         TopicLink=(TOPICLINK *)(buffer+i+j);
  3384.                         NextLinkData1=NextTopicLink+21;
  3385.                         NextLinkData2=NextTopicLink+TopicLink->DataLen1;
  3386.                         NextBlockEnd=NextTopicLink+TopicLink->BlockSize;
  3387.                         if(NextLinkData1>NextBlockEnd) NextLinkData1=NextBlockEnd;
  3388.                         if(NextLinkData2>NextBlockEnd) NextLinkData2=NextBlockEnd;
  3389.                         NextTopicLink+=TopicLink->BlockSize;
  3390.                     }
  3391.                     if(i+j==NextLinkData1) textcolor(color=5);
  3392.                     if(i+j==NextLinkData2) textcolor(color=3);
  3393.                     if(i+j==NextBlockEnd) textcolor(color=1);
  3394.                     cprintf("%02X ",(unsigned char)buffer[i+j]);
  3395.                 }
  3396.                 textcolor(7);
  3397.                 for(j=0;j<16-n;j++) cprintf("   ");
  3398.                 for(j=0;j<n;j++) cprintf("%c",isprint((unsigned char)buffer[i+j])?buffer[i+j]:'.');
  3399.                 printf("\n");
  3400.             }
  3401.             NextTopicLink-=BufferSize-12;
  3402.             NextLinkData1-=BufferSize-12;
  3403.             NextLinkData2-=BufferSize-12;
  3404.             NextBlockEnd-=BufferSize-12;
  3405.         }
  3406.     }
  3407. }
  3408.  
  3409. /**************************************************
  3410.    Phrase replacement of a string from topic record.
  3411.    Expands to out, returns end of expanded string.
  3412.    Expands to stdout if out==NULL, returns NULL then.
  3413. ****************************************************/
  3414. char *StringPrint(unsigned char *String,long Length,char *out)
  3415. {
  3416.     int CurChar;
  3417.  
  3418.     if(NewPhrases)
  3419.     {
  3420.         while(Length)
  3421.         {
  3422.             CurChar=*String++;
  3423.             Length--;
  3424.             if((CurChar&1)==0) // phrases 0..127
  3425.             {
  3426.                 out=PrintPhrase(CurChar/2,out);
  3427.             }
  3428.             else if((CurChar&3)==1) // phrases 128..16511
  3429.             {
  3430.                 CurChar=128+(CurChar/4)*256+*String++;
  3431.                 Length--;
  3432.                 out=PrintPhrase(CurChar,out);
  3433.             }
  3434.             else if((CurChar&7)==3) // copy next n characters
  3435.             {
  3436.                 while(CurChar>0)
  3437.                 {
  3438.                     if(out)
  3439.                     {
  3440.                         *out++=*String++;
  3441.                     }
  3442.                     else if(isprint((unsigned char)*String))
  3443.                     {
  3444.                         putchar(*String++);
  3445.                     }
  3446.                     else
  3447.                     {
  3448.                         printf("(%02x)",*String++);
  3449.                     }
  3450.                     Length--;
  3451.                     CurChar-=8;
  3452.                 }
  3453.             }
  3454.             else if((CurChar&0x0F)==0x07)
  3455.             {
  3456.                 while(CurChar>0)
  3457.                 {
  3458.                     if(out)
  3459.                     {
  3460.                         *out++=' ';
  3461.                     }
  3462.                     else
  3463.                     {
  3464.                         printf(" ");
  3465.                     }
  3466.                     CurChar-=16;
  3467.                 }
  3468.             }
  3469.             else // if((CurChar&0x0F)==0x0F)
  3470.             {
  3471.                 while(CurChar>0)
  3472.                 {
  3473.                     if(out)
  3474.                     {
  3475.                         *out++='\0';
  3476.                     }
  3477.                     else
  3478.                     {
  3479.                         printf("(00)");
  3480.                     }
  3481.                     CurChar-=16;
  3482.                 }
  3483.             }
  3484.         }
  3485.     }
  3486.     else
  3487.     {
  3488.         while(Length)
  3489.         {
  3490.             CurChar=*String++;
  3491.             Length--;
  3492.             if(CurChar>0&&CurChar<16) // phrase 0..1919
  3493.             {
  3494.                 CurChar=256*(CurChar-1)+*String++;
  3495.                 Length--;
  3496.                 out=PrintPhrase(CurChar/2,out);
  3497.                 if(CurChar&1)
  3498.                 {
  3499.                     if(out)
  3500.                     {
  3501.                         *out++=' ';
  3502.                     }
  3503.                     else
  3504.                     {
  3505.                         putchar(' ');
  3506.                     }
  3507.                 }
  3508.             }
  3509.             else if(out)
  3510.             {
  3511.                 *out++=CurChar;
  3512.             }
  3513.             else if(isprint((unsigned char)CurChar))
  3514.             {
  3515.                 putchar(CurChar);
  3516.             }
  3517.             else
  3518.             {
  3519.                 printf("(%02x)",CurChar);
  3520.             }
  3521.         }
  3522.     }
  3523.     if(out) *out='\0';
  3524.     return out;
  3525. }
  3526.  
  3527. void putrtf(FILE *rtf,char *str)
  3528. {
  3529.     if(rtf) while(*str)
  3530.     {
  3531.         if(*str=='{'||*str=='}'||*str=='\\')
  3532.         {
  3533.             putc('\\',rtf);
  3534.             putc(*str++,rtf);
  3535.         }
  3536.         else if(isprint((unsigned char)*str))
  3537.         {
  3538.             putc(*str++,rtf);
  3539.         }
  3540.         else
  3541.         {
  3542.             fprintf(rtf,"\\'%02x",(unsigned char)*str++);
  3543.         }
  3544.     }
  3545. }
  3546.  
  3547. int NextKeywordRec,KeywordRecs;
  3548. typedef struct
  3549. {
  3550.     char Footnote;
  3551.     char *Keyword;
  3552.     long TopicOffset;
  3553. }
  3554. KEYWORDREC;
  3555. KEYWORDREC *KeywordRec;
  3556.  
  3557. int KeywordRecCmp(const void *a,const void *b)
  3558. {
  3559.     const KEYWORDREC *A;
  3560.     const KEYWORDREC *B;
  3561.  
  3562.     A=(const KEYWORDREC *)a;
  3563.     B=(const KEYWORDREC *)b;
  3564.     if(A->TopicOffset<B->TopicOffset) return -1;
  3565.     if(A->TopicOffset>B->TopicOffset) return 1;
  3566.     return 0;
  3567. }
  3568.  
  3569. long NextTopicOffset(FILE *HelpFile,long topic)
  3570. {
  3571.     long pos,FileStart;
  3572.     char Title[256];
  3573.     int entries,count;
  3574.     long TopicOffset;
  3575.     BUFFER buf;
  3576.  
  3577.     pos=ftell(HelpFile);
  3578.     if(SearchFile(HelpFile,"|TTLBTREE",&FileStart))
  3579.     {
  3580.         entries=GetFirstPage(HelpFile,FileStart,&buf);
  3581.         while(entries)
  3582.         {
  3583.             for(count=1;count<=entries;count++)
  3584.             {
  3585.                 myFRead(&TopicOffset,sizeof(TopicOffset),HelpFile);
  3586.                 if(TopicOffset>topic)
  3587.                 {
  3588.                     fseek(HelpFile,pos,SEEK_SET);
  3589.                     return TopicOffset;
  3590.                 }
  3591.                 StringRead(Title,sizeof(Title),HelpFile);
  3592.             }
  3593.             entries=GetNextPage(HelpFile,&buf);
  3594.         }
  3595.     }
  3596.     fseek(HelpFile,pos,SEEK_SET);
  3597.     return 0x7FFFFFFFL;
  3598. }
  3599.  
  3600. void CollectKeywords(FILE *HelpFile,long from,long upto)
  3601. {
  3602.     FILEHEADER FileHdr;
  3603.     unsigned short count,i;
  3604.     long FileStart,TreeStart,savepos;
  3605.     char Keyword[256];  // variable length keyword
  3606.     short Count;       // count of keywords occurances
  3607.     long KWDataOffset; // offset into |KWDATA file
  3608.     long *keytopic;
  3609.     BUFFER buf;
  3610.     short entries;
  3611.     int map,n;
  3612.     char data[10];
  3613.     char tree[10];
  3614.  
  3615.     if(KeywordRecs&&KeywordRec) // free old keywords
  3616.     {
  3617.         for(i=0;i<KeywordRecs;i++)
  3618.         {
  3619.             if(KeywordRec[i].Keyword) free(KeywordRec[i].Keyword);
  3620.         }
  3621.         free(KeywordRec);
  3622.         KeywordRec=NULL;
  3623.         NextKeywordRec=KeywordRecs=0;
  3624.     }
  3625.     savepos=ftell(HelpFile);
  3626.     for(n=0;n<2;n++) for(map='A';map<='z';map++) if(n==0&&lists[map-'A']||n==1&&keyindex[map-'A'])
  3627.     {
  3628.         if(n)
  3629.         {
  3630.             sprintf(data,"|%cKWDATA",map);
  3631.             sprintf(tree,"|%cKWBTREE",map);
  3632.         }
  3633.         else
  3634.         {
  3635.             sprintf(data,"|%cWDATA",map);
  3636.             sprintf(tree,"|%cWBTREE",map);
  3637.         }
  3638.         if(SearchFile(HelpFile,data,&FileStart)&&SearchFile(HelpFile,tree,&TreeStart))
  3639.         {
  3640.             fseek(HelpFile,FileStart,SEEK_SET);
  3641.             myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  3642.             keytopic=myMalloc(FileHdr.FileSize);
  3643.             myFRead(keytopic,FileHdr.FileSize,HelpFile);
  3644.             entries=GetFirstPage(HelpFile,TreeStart,&buf);
  3645.             while(entries)
  3646.             {
  3647.                 for(count=1;count<=entries;count++)
  3648.                 {
  3649.                    StringRead(Keyword,sizeof(Keyword),HelpFile);
  3650.                    myFRead(&Count,sizeof(Count),HelpFile);
  3651.                    myFRead(&KWDataOffset,sizeof(KWDataOffset),HelpFile);
  3652.                    for(i=0;i<Count;i++)
  3653.                    {
  3654.                        if(keytopic[KWDataOffset/4+i]>=from&&keytopic[KWDataOffset/4+i]<upto)
  3655.                        {
  3656.                            KeywordRec=myReAlloc(KeywordRec,(KeywordRecs+1)*sizeof(KEYWORDREC));
  3657.                            KeywordRec[KeywordRecs].Footnote=map;
  3658.                            KeywordRec[KeywordRecs].Keyword=myStrDup(Keyword);
  3659.                            KeywordRec[KeywordRecs].TopicOffset=keytopic[KWDataOffset/4+i];
  3660.                            KeywordRecs++;
  3661.                        }
  3662.                    }
  3663.                 }
  3664.                 entries=GetNextPage(HelpFile,&buf);
  3665.             }
  3666.             free(keytopic);
  3667.         }
  3668.         if(KeywordRec) qsort(KeywordRec,KeywordRecs,sizeof(KEYWORDREC),KeywordRecCmp);
  3669.     }
  3670.     fseek(HelpFile,savepos,SEEK_SET);
  3671. }
  3672.  
  3673. void ListKeywords(FILE *HelpFile,FILE *rtf,long topic)
  3674. {
  3675.     FILEHEADER FileHdr;
  3676.     unsigned short count,i;
  3677.     long FileStart,TreeStart,savepos;
  3678.     char Keyword[256];  // variable length keyword
  3679.     short Count;       // count of keywords occurances
  3680.     long KWDataOffset; // offset into |KWDATA file
  3681.     long *keytopic;
  3682.     BUFFER buf;
  3683.     short entries;
  3684.     int map,n;
  3685.     BOOL first;
  3686.     char data[10];
  3687.     char tree[10];
  3688.  
  3689.     savepos=ftell(HelpFile);
  3690.     for(n=0;n<2;n++) for(map='A';map<='z';map++) if(n==0&&lists[map-'A']||n==1&&keyindex[map-'A'])
  3691.     {
  3692.         if(n)
  3693.         {
  3694.             sprintf(data,"|%cKWDATA",map);
  3695.             sprintf(tree,"|%cKWBTREE",map);
  3696.         }
  3697.         else
  3698.         {
  3699.             sprintf(data,"|%cWDATA",map);
  3700.             sprintf(tree,"|%cWBTREE",map);
  3701.         }
  3702.         if(SearchFile(HelpFile,data,&FileStart)&&SearchFile(HelpFile,tree,&TreeStart))
  3703.         {
  3704.             first=TRUE;
  3705.             fseek(HelpFile,FileStart,SEEK_SET);
  3706.             myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  3707.             keytopic=myMalloc(FileHdr.FileSize);
  3708.             myFRead(keytopic,FileHdr.FileSize,HelpFile);
  3709.             entries=GetFirstPage(HelpFile,TreeStart,&buf);
  3710.             while(entries)
  3711.             {
  3712.                 for(count=1;count<=entries;count++)
  3713.                 {
  3714.                    StringRead(Keyword,sizeof(Keyword),HelpFile);
  3715.                    myFRead(&Count,sizeof(Count),HelpFile);
  3716.                    myFRead(&KWDataOffset,sizeof(KWDataOffset),HelpFile);
  3717.                    for(i=0;i<Count;i++)
  3718.                    {
  3719.                        if(keytopic[KWDataOffset/4+i]==topic)
  3720.                        {
  3721.                            if(first)
  3722.                            {
  3723.                                if(n) fprintf(rtf,"K");
  3724.                                fprintf(rtf,"%c{\\footnote %c ",map,map);
  3725.                                first=FALSE;
  3726.                            }
  3727.                            else
  3728.                            {
  3729.                                fprintf(rtf,"; ");
  3730.                            }
  3731.                            putrtf(rtf,Keyword);
  3732.                        }
  3733.                    }
  3734.                 }
  3735.                 entries=GetNextPage(HelpFile,&buf);
  3736.             }
  3737.             if(!first) fprintf(rtf,"}\n");
  3738.             free(keytopic);
  3739.         }
  3740.     }
  3741.     fseek(HelpFile,savepos,SEEK_SET);
  3742. }
  3743.  
  3744. void ListWindows(FILE *HelpFile,FILE *rtf,long topic)
  3745. {
  3746.     long savepos;
  3747.     static int entries;
  3748.     static long offset,value;
  3749.     static BUFFER buf;
  3750.     long FileStart;
  3751.     static int VIOLAfound=-1;
  3752.  
  3753.     if(VIOLAfound==0) return;
  3754.     savepos=ftell(HelpFile);
  3755.     if(VIOLAfound==-1)
  3756.     {
  3757.         VIOLAfound=0;
  3758.         if(SearchFile(HelpFile,"|VIOLA",&FileStart))
  3759.         {
  3760.             entries=GetFirstPage(HelpFile,FileStart,&buf);
  3761.             if(entries)
  3762.             {
  3763.                 myFRead(&offset,sizeof(offset),HelpFile);
  3764.                 myFRead(&value,sizeof(value),HelpFile);
  3765.                 VIOLAfound=1;
  3766.                 entries--;
  3767.             }
  3768.         }
  3769.     }
  3770.     if(VIOLAfound==1)
  3771.     {
  3772.         while(topic>offset)
  3773.         {
  3774.             if(!entries)
  3775.             {
  3776.                 entries=GetNextPage(HelpFile,&buf);
  3777.                 if(entries==0)
  3778.                 {
  3779.                     VIOLAfound=0;
  3780.                     break;
  3781.                 }
  3782.             }
  3783.             myFRead(&offset,sizeof(offset),HelpFile);
  3784.             myFRead(&value,sizeof(value),HelpFile);
  3785.             entries--;
  3786.         }
  3787.         if(offset==topic)
  3788.         {
  3789.             fprintf(rtf,">{\\footnote > %s}\n",WindowName(value));
  3790.         }
  3791.     }
  3792.     fseek(HelpFile,savepos,SEEK_SET);
  3793. }
  3794.  
  3795. void AddStart(long StartTopic,int BrowseNum,int Count)
  3796. {
  3797.     start=myReAlloc(start,(starts+1)*sizeof(START));
  3798.     start[starts].StartTopic=StartTopic;
  3799.     start[starts].BrowseNum=BrowseNum;
  3800.     start[starts].Start=Count;
  3801.     starts++;
  3802. }
  3803.  
  3804. void FixStart(int BrowseNum,int NewBrowseNum,int AddCount)
  3805. {
  3806.     int i;
  3807.  
  3808.     for(i=0;i<starts;i++) if(start[i].BrowseNum==BrowseNum)
  3809.     {
  3810.         start[i].BrowseNum=NewBrowseNum;
  3811.         start[i].Start+=AddCount;
  3812.     }
  3813. }
  3814.  
  3815. void AddBrowse(long StartTopic,long NextTopic,long PrevTopic)
  3816. {
  3817.     int i;
  3818.  
  3819.     for(i=0;i<browses;i++) if(browse[i].StartTopic==-1L) break; // empty space in array ?
  3820.     if(i==browses) // no empty space, add to array
  3821.     {
  3822.         browse=myReAlloc(browse,++browses*sizeof(BROWSE));
  3823.     }
  3824.     browse[i].StartTopic=StartTopic;
  3825.     browse[i].NextTopic=NextTopic;
  3826.     browse[i].PrevTopic=PrevTopic;
  3827.     browse[i].BrowseNum=browsenums++;
  3828.     browse[i].Start=1;
  3829.     browse[i].Count=1;
  3830. }
  3831.  
  3832. void MergeBrowse(long TopicOffset,long NextTopic,long PrevTopic)
  3833. {
  3834.     int i,j;
  3835.  
  3836.     for(i=0;i<browses;i++) if(browse[i].StartTopic!=-1L)
  3837.     {
  3838.         if(browse[i].NextTopic==TopicOffset) break;
  3839.     }
  3840.     for(j=0;j<browses;j++) if(browse[j].StartTopic!=-1L)
  3841.     {
  3842.         if(browse[j].PrevTopic==TopicOffset) break;
  3843.     }
  3844.     if(i<browses&&j<browses)
  3845.     {
  3846.         browse[i].Count++;
  3847.         browse[i].NextTopic=browse[j].NextTopic;
  3848.         FixStart(browse[j].BrowseNum,browse[i].BrowseNum,browse[i].Count);
  3849.         browse[j].Start+=browse[i].Count;
  3850.         AddStart(browse[j].StartTopic,browse[i].BrowseNum,browse[j].Start);
  3851.         browse[i].Count+=browse[j].Count;
  3852.         browse[j].StartTopic=-1L;
  3853.         if(browse[i].NextTopic==-1L&&browse[i].PrevTopic==-1L)
  3854.         {
  3855.             AddStart(browse[i].StartTopic,browse[i].BrowseNum,browse[i].Start);
  3856.             browse[i].StartTopic=-1L;
  3857.         }
  3858.     }
  3859.     else
  3860.     {
  3861.         warnings=TRUE;
  3862.         if(warn) fprintf(stderr,"\nCan not merge %08lx %08lx %08lx\n",TopicOffset,NextTopic,PrevTopic);
  3863.     }
  3864. }
  3865.  
  3866. void LinkBrowse(long TopicOffset,long NextTopic,long PrevTopic)
  3867. {
  3868.     int i;
  3869.  
  3870.     for(i=0;i<browses;i++) if(browse[i].StartTopic!=-1L)
  3871.     {
  3872.         if(browse[i].NextTopic==TopicOffset) break;
  3873.     }
  3874.     if(i<browses)
  3875.     {
  3876.         browse[i].NextTopic=NextTopic;
  3877.         browse[i].Count++;
  3878.         if(browse[i].NextTopic==-1L&&browse[i].PrevTopic==-1L)
  3879.         {
  3880.             AddStart(browse[i].StartTopic,browse[i].BrowseNum,browse[i].Start);
  3881.             browse[i].StartTopic=-1L;
  3882.         }
  3883.     }
  3884.     else
  3885.     {
  3886.         warnings=TRUE;
  3887.         if(warn)
  3888.         {
  3889.             fprintf(stderr,"\nCan not link %08lx %08lx %08lx\n",TopicOffset,NextTopic,PrevTopic);
  3890.             for(i=0;i<browses;i++) if(browse[i].StartTopic!=-1L)
  3891.             {
  3892.                 fprintf(stderr,"Open %08lx %08lx\n",browse[i].PrevTopic,browse[i].NextTopic);
  3893.             }
  3894.         }
  3895.     }
  3896. }
  3897.  
  3898. void BackLinkBrowse(long TopicOffset,long NextTopic,long PrevTopic)
  3899. {
  3900.     int i;
  3901.  
  3902.     for(i=0;i<browses;i++) if(browse[i].StartTopic!=-1L)
  3903.     {
  3904.         if(browse[i].PrevTopic==TopicOffset) break;
  3905.     }
  3906.     if(i<browses)
  3907.     {
  3908.         browse[i].PrevTopic=PrevTopic;
  3909.         browse[i].Count++;
  3910.         browse[i].Start++;
  3911.         FixStart(browse[i].BrowseNum,browse[i].BrowseNum,1);
  3912.         if(browse[i].NextTopic==-1L&&browse[i].PrevTopic==-1L)
  3913.         {
  3914.             AddStart(browse[i].StartTopic,browse[i].BrowseNum,browse[i].Start);
  3915.             browse[i].StartTopic=-1L;
  3916.         }
  3917.     }
  3918.     else
  3919.     {
  3920.         warnings=TRUE;
  3921.         if(warn) fprintf(stderr,"\nCan not backlink %08lx %08lx %08lx\n",TopicOffset,NextTopic,PrevTopic);
  3922.     }
  3923. }
  3924.  
  3925. unsigned long AddLink(long StartTopic,long NextTopic,long PrevTopic)
  3926. {
  3927.     int i,j;
  3928.     unsigned long result;
  3929.  
  3930.     result=0L;
  3931.     for(i=0;i<browses;i++) if(browse[i].StartTopic==-1L) break;
  3932.     if(i==browses) browse=myReAlloc(browse,++browses*sizeof(BROWSE));
  3933.     for(j=0;j<starts;j++) if(start[j].StartTopic==StartTopic) break;
  3934.     if(j<starts)
  3935.     {
  3936.         browse[i].StartTopic=start[j].StartTopic;
  3937.         browse[i].BrowseNum=start[j].BrowseNum;
  3938.         browse[i].Start=start[j].Start;
  3939.         browse[i].Count=start[j].Start;
  3940.         browse[i].NextTopic=NextTopic;
  3941.         browse[i].PrevTopic=PrevTopic;
  3942.         result=browse[i].BrowseNum+((long)browse[i].Start<<16);
  3943.     }
  3944.     else
  3945.     {
  3946.         warnings=TRUE;
  3947.         if(warn) fprintf(stderr,"\nBrowse start %08lx not found\n",StartTopic);
  3948.     }
  3949.     return result;
  3950. }
  3951.  
  3952. unsigned long MergeLink(long TopicOffset,long NextTopic,long PrevTopic)
  3953. {
  3954.     int i,j;
  3955.     unsigned long result;
  3956.  
  3957.     result=0L;
  3958.     for(i=0;i<browses;i++) if(browse[i].StartTopic!=-1L)
  3959.     {
  3960.         if(browse[i].NextTopic==TopicOffset) break;
  3961.     }
  3962.     for(j=0;j<browses;j++) if(browse[j].StartTopic!=-1L)
  3963.     {
  3964.         if(browse[j].PrevTopic==TopicOffset) break;
  3965.     }
  3966.     if(i<browses&&j<browses)
  3967.     {
  3968.         browse[i].Count++;
  3969.         browse[j].Start--;
  3970.         if(browse[i].Count!=browse[j].Start)
  3971.         {
  3972.             warnings=TRUE;
  3973.             if(warn) fprintf(stderr,"\nPrev browse end %d doen't match next browse start %d\n",browse[i].Count,browse[j].Start);
  3974.         }
  3975.         result=browse[i].BrowseNum+((long)browse[i].Count<<16);
  3976.         browse[i].NextTopic=browse[j].NextTopic;
  3977.         browse[i].Count=browse[j].Count;
  3978.         browse[j].StartTopic=-1L;
  3979.     }
  3980.     else
  3981.     {
  3982.         warnings=TRUE;
  3983.         if(warn) fprintf(stderr,"\nCan not merge %08lx %08lx %08lx\n",TopicOffset,NextTopic,PrevTopic);
  3984.     }
  3985.     return result;
  3986. }
  3987.  
  3988. unsigned long LinkLink(long TopicOffset,long NextTopic,long PrevTopic)
  3989. {
  3990.     int i;
  3991.     unsigned long result;
  3992.  
  3993.     result=0L;
  3994.     for(i=0;i<browses;i++) if(browse[i].StartTopic!=-1L)
  3995.     {
  3996.         if(browse[i].NextTopic==TopicOffset) break;
  3997.     }
  3998.     if(i<browses)
  3999.     {
  4000.         browse[i].NextTopic=NextTopic;
  4001.         browse[i].Count++;
  4002.         result=browse[i].BrowseNum+((long)browse[i].Count<<16);
  4003.         if(browse[i].NextTopic==-1L&&browse[i].PrevTopic==-1L)
  4004.         {
  4005.             browse[i].StartTopic=-1L;
  4006.         }
  4007.     }
  4008.     else
  4009.     {
  4010.         warnings=TRUE;
  4011.         if(warn) fprintf(stderr,"\nCan not link %08lx %08lx %08lx\n",TopicOffset,NextTopic,PrevTopic);
  4012.     }
  4013.     return result;
  4014. }
  4015.  
  4016. unsigned long BackLinkLink(long TopicOffset,long NextTopic,long PrevTopic)
  4017. {
  4018.     int i;
  4019.     unsigned long result;
  4020.  
  4021.     result=0L;
  4022.     for(i=0;i<browses;i++) if(browse[i].StartTopic!=-1L)
  4023.     {
  4024.         if(browse[i].PrevTopic==TopicOffset) break;
  4025.     }
  4026.     if(i<browses)
  4027.     {
  4028.         browse[i].PrevTopic=PrevTopic;
  4029.         browse[i].Start--;
  4030.         result=browse[i].BrowseNum+((long)browse[i].Start<<16);
  4031.         if(browse[i].NextTopic==-1L&&browse[i].PrevTopic==-1L)
  4032.         {
  4033.             browse[i].StartTopic=-1L;
  4034.         }
  4035.     }
  4036.     else
  4037.     {
  4038.         warnings=TRUE;
  4039.         if(warn) fprintf(stderr,"\nCan not backlink %08lx %08lx %08lx\n",TopicOffset,NextTopic,PrevTopic);
  4040.     }
  4041.     return result;
  4042. }
  4043.  
  4044. char *scanint(char *ptr,short *val)
  4045. {
  4046.     if(*ptr&1)
  4047.     {
  4048.         *val=(*(unsigned short *)(ptr)>>1)-0x4000;
  4049.         ptr+=2;
  4050.     }
  4051.     else
  4052.     {
  4053.         *val=(*(unsigned char *)(ptr)>>1)-0x40;
  4054.         ptr++;
  4055.     }
  4056.     return ptr;
  4057. }
  4058. char *scanword(char *ptr,unsigned short *val)
  4059. {
  4060.     if(*ptr&1)
  4061.     {
  4062.         *val=*(unsigned short *)(ptr)>>1;
  4063.         ptr+=2;
  4064.     }
  4065.     else
  4066.     {
  4067.         *val=*(unsigned char *)(ptr)>>1;
  4068.         ptr++;
  4069.     }
  4070.     return ptr;
  4071. }
  4072. char *scanlong(char *ptr,long *val)
  4073. {
  4074.     if(*(short *)ptr&1)
  4075.     {
  4076.         *val=(*(unsigned long *)(ptr)>>1)-0x40000000L;
  4077.         ptr+=4;
  4078.     }
  4079.     else
  4080.     {
  4081.         *val=(*(unsigned short *)(ptr)>>1)-0x4000;
  4082.         ptr+=2;
  4083.     }
  4084.     return ptr;
  4085. }
  4086. char *scandword(char *ptr,unsigned long *val)
  4087. {
  4088.     if(*(short *)ptr&1)
  4089.     {
  4090.         *val=*(unsigned long *)(ptr)>>1;
  4091.         ptr+=2;
  4092.     }
  4093.     else
  4094.     {
  4095.         *val=*(unsigned short *)(ptr)>>1;
  4096.         ptr+=2;
  4097.     }
  4098.     return ptr;
  4099. }
  4100. void ChangeFont(FILE *rtf,int i,BOOL ul,BOOL uldb)
  4101. {
  4102.     if(i>=0)
  4103.     {
  4104.         if(fonts&&i<fonts)
  4105.         {
  4106.             fprintf(rtf,"}{\\f%d",font[i].FontName);
  4107.             if(font[i].Attributes&FONT_ITAL) fprintf(rtf,"\\i");
  4108.             if(font[i].Attributes&FONT_BOLD) fprintf(rtf,"\\b");
  4109.             if(ul||(font[i].Attributes&FONT_UNDR)) fprintf(rtf,"\\ul");
  4110.             if(font[i].Attributes&FONT_STRK) fprintf(rtf,"\\strike");
  4111.             if(uldb||(font[i].Attributes&FONT_DBUN)) fprintf(rtf,"\\uldb");
  4112.             if(font[i].Attributes&FONT_SMCP) fprintf(rtf,"\\scaps");
  4113.             fprintf(rtf,"\\fs%d",font[i].HalfPoints);
  4114.             fprintf(rtf,"\\cf%d ",font[i].FGRGB[0]);
  4115.         }
  4116.         else if(newfonts&&i<newfonts)
  4117.         {
  4118.             fprintf(rtf,"}{\\f%d",newfont[i].FontName);
  4119.             if(newfont[i].Italic) fprintf(rtf,"\\i");
  4120.             if(newfont[i].Weight>600) fprintf(rtf,"\\b");
  4121.             if(ul||newfont[i].Underline) fprintf(rtf,"\\ul");
  4122.             if(newfont[i].StrikeOut) fprintf(rtf,"\\strike");
  4123.             if(uldb||newfont[i].DoubleUnderline) fprintf(rtf,"\\uldb");
  4124.             if(newfont[i].SmallCaps) fprintf(rtf,"\\scaps");
  4125.             fprintf(rtf,"\\fs%ld",-2*newfont[i].Height);
  4126.             fprintf(rtf,"\\cf%d ",newfont[i].FGRGB[0]);
  4127.         }
  4128.     }
  4129. }
  4130.  
  4131. void TopicDump(FILE *HelpFile,FILE *rtf,BOOL doc)
  4132. {
  4133.     TOPICLINK TopicLink;
  4134.     TOPICLINK NextTopicLink;
  4135.     char *LinkData1;  // Data associated with this link
  4136.     long nonscroll=-1L;
  4137.     char *LinkData2;  // Second set of data
  4138.     char *end;
  4139.     int fontset;
  4140.     long FileOffset;
  4141.     int NextContextRec;
  4142.     unsigned long BrowseNum;
  4143.     int BytesRead;
  4144.     long counter=0L;
  4145.     char *hotspot;
  4146.     char *arg;
  4147.     int i;
  4148.     long offset;
  4149.     long pos;
  4150.     BOOL firsttopic=TRUE;
  4151.     BOOL ul,uldb;
  4152.     int nextbitmap;
  4153.     long TopicNum;
  4154.  
  4155.     if(SearchFile(HelpFile,"|TOPIC",&FileOffset))
  4156.     {
  4157.         fseek(HelpFile,FileOffset,SEEK_SET);
  4158.         dontCount=FALSE;
  4159.         fontset=-1;
  4160.         nextbitmap=1;
  4161.         if(browse) free(browse);
  4162.         browse=NULL;
  4163.         browses=0;
  4164.         NextContextRec=0;
  4165.         TopicNum=16;
  4166.         ul=uldb=FALSE;
  4167.         hotspot=NULL;
  4168.         BytesRead=(int)TopicRead(HelpFile,12L,&TopicLink,sizeof(TopicLink));
  4169.         while(BytesRead==sizeof(TOPICLINK))
  4170.         {
  4171.             pos=TopicPos;
  4172.             offset=TopicOffset;
  4173.             if(TopicLink.DataLen1>sizeof(TOPICLINK))
  4174.             {
  4175.                 LinkData1=myMalloc(TopicLink.DataLen1-sizeof(TOPICLINK)+1);
  4176.                 TopicRead(HelpFile,0L,LinkData1,TopicLink.DataLen1-sizeof(TOPICLINK));
  4177.             }
  4178.             else
  4179.             {
  4180.                 LinkData1=NULL;
  4181.             }
  4182.             if(TopicLink.DataLen1<TopicLink.BlockSize) // read LinkData2 without phrase replacement
  4183.             {
  4184.                 LinkData2=myMalloc(TopicLink.BlockSize-TopicLink.DataLen1+1);
  4185.                 TopicRead(HelpFile,0L,LinkData2,TopicLink.BlockSize-TopicLink.DataLen1);
  4186.             }
  4187.             else
  4188.             {
  4189.                 LinkData2=NULL;
  4190.             }
  4191.             BytesRead=(int)TopicRead(HelpFile,0L,&NextTopicLink,sizeof(NextTopicLink));
  4192.             if(LinkData1&&TopicLink.RecordType==TL_TOPICHDR) // display a Topic Header record
  4193.             {
  4194.                 if(!firsttopic) fprintf(rtf,"\\page\n");
  4195.                 firsttopic=FALSE;
  4196.                 fprintf(stderr,".");
  4197.                 if(!doc)
  4198.                 {
  4199.                     BrowseNum=0L;
  4200.                     if(before31)
  4201.                     {
  4202.                         TOPICHEADER30 *TopicHdr;
  4203.  
  4204.                         TopicHdr=(TOPICHEADER30 *)LinkData1;
  4205.                         fprintf(rtf,"{#{\\footnote # TOPIC%ld}}\n",TopicNum);
  4206.                         if(resolvebrowse)
  4207.                         {
  4208.                             if(TopicHdr->NextTopicNum>TopicNum&&TopicHdr->PrevTopicNum>TopicNum
  4209.                             || TopicHdr->NextTopicNum==-1&&TopicHdr->PrevTopicNum>TopicNum
  4210.                             || TopicHdr->NextTopicNum>TopicNum&&TopicHdr->PrevTopicNum==-1)
  4211.                             {
  4212.                                 BrowseNum=AddLink(TopicNum,TopicHdr->NextTopicNum,TopicHdr->PrevTopicNum);
  4213.                             }
  4214.                             else if(TopicHdr->NextTopicNum!=-1&&TopicHdr->NextTopicNum<TopicNum&&TopicHdr->PrevTopicNum!=-1&&TopicHdr->PrevTopicNum<TopicNum)
  4215.                             {
  4216.                                 BrowseNum=MergeLink(TopicNum,TopicHdr->NextTopicNum,TopicHdr->PrevTopicNum);
  4217.                             }
  4218.                             else if(TopicHdr->NextTopicNum!=-1&&TopicHdr->NextTopicNum<TopicNum&&(TopicHdr->PrevTopicNum==-1||TopicHdr->PrevTopicNum>TopicNum))
  4219.                             {
  4220.                                 BrowseNum=BackLinkLink(TopicNum,TopicHdr->NextTopicNum,TopicHdr->PrevTopicNum);
  4221.                             }
  4222.                             else if(TopicHdr->PrevTopicNum!=-1&&TopicHdr->PrevTopicNum<TopicNum&&(TopicHdr->NextTopicNum==-1||TopicHdr->NextTopicNum>TopicNum))
  4223.                             {
  4224.                                 BrowseNum=LinkLink(TopicNum,TopicHdr->NextTopicNum,TopicHdr->PrevTopicNum);
  4225.                             }
  4226.                         }
  4227.                         ListKeywords(HelpFile,rtf,pos);
  4228.                         TopicNum++;
  4229.                     }
  4230.                     else
  4231.                     {
  4232.                         TOPICHEADER *TopicHdr;
  4233.  
  4234.                         TopicHdr=(TOPICHEADER *)LinkData1;
  4235.                         if(TopicHdr->NonScroll!=-1L)
  4236.                         {
  4237.                             if(TopicHdr->Scroll!=-1L)
  4238.                             {
  4239.                                 nonscroll=TopicHdr->Scroll;
  4240.                             }
  4241.                             else
  4242.                             {
  4243.                                 nonscroll=TopicHdr->NextTopic;
  4244.                             }
  4245.                         }
  4246.                         else
  4247.                         {
  4248.                             nonscroll=-1L;
  4249.                         }
  4250.                         pos=offset; // topic offset to link keywords to
  4251.                         while(NextContextRec<ContextRecs&&ContextRec[NextContextRec].TopicOffset<offset)
  4252.                         {
  4253.                             warnings=TRUE;
  4254.                             if(warn) fprintf(stderr,"\noffset %08lx behind %08lx\n",offset,ContextRec[NextContextRec].TopicOffset);
  4255.                             fprintf(rtf,"{#{\\footnote # %s}}\n",unhash(ContextRec[NextContextRec].HashValue));
  4256.                             while(NextContextRec<ContextRecs&&ContextRec[NextContextRec].TopicOffset==ContextRec[NextContextRec+1].TopicOffset)
  4257.                             {
  4258.                                 NextContextRec++;
  4259.                             }
  4260.                             NextContextRec++;
  4261.                         }
  4262.                         if(NextContextRec<ContextRecs&&ContextRec[NextContextRec].TopicOffset==offset)
  4263.                         {
  4264.                             fprintf(rtf,"{#{\\footnote # %s}}\n",unhash(ContextRec[NextContextRec].HashValue));
  4265.                             while(NextContextRec<ContextRecs&&ContextRec[NextContextRec].TopicOffset==ContextRec[NextContextRec+1].TopicOffset)
  4266.                             {
  4267.                                 NextContextRec++;
  4268.                             }
  4269.                             NextContextRec++;
  4270.                         }
  4271.                         if(resolvebrowse)
  4272.                         {
  4273.                             if(TopicHdr->BrowseFor>offset&&TopicHdr->BrowseBck>offset
  4274.                             || TopicHdr->BrowseFor==-1L&&TopicHdr->BrowseBck>offset
  4275.                             || TopicHdr->BrowseFor>offset&&TopicHdr->BrowseBck==-1L)
  4276.                             {
  4277.                                 BrowseNum=AddLink(offset,TopicHdr->BrowseFor,TopicHdr->BrowseBck);
  4278.                             }
  4279.                             else if(TopicHdr->BrowseFor!=-1L&&TopicHdr->BrowseFor<offset&&TopicHdr->BrowseBck!=-1L&&TopicHdr->BrowseBck<offset)
  4280.                             {
  4281.                                 BrowseNum=MergeLink(offset,TopicHdr->BrowseFor,TopicHdr->BrowseBck);
  4282.                             }
  4283.                             else if(TopicHdr->BrowseFor!=-1L&&TopicHdr->BrowseFor<offset&&(TopicHdr->BrowseBck==-1L||TopicHdr->BrowseBck>offset))
  4284.                             {
  4285.                                 BrowseNum=BackLinkLink(offset,TopicHdr->BrowseFor,TopicHdr->BrowseBck);
  4286.                             }
  4287.                             else if(TopicHdr->BrowseBck!=-1L&&TopicHdr->BrowseBck<offset&&(TopicHdr->BrowseFor==-1L||TopicHdr->BrowseFor>offset))
  4288.                             {
  4289.                                 BrowseNum=LinkLink(offset,TopicHdr->BrowseFor,TopicHdr->BrowseBck);
  4290.                             }
  4291.                         }
  4292.                         CollectKeywords(HelpFile,pos,NextTopicOffset(HelpFile,pos));
  4293.                     }
  4294.                     if(BrowseNum) fprintf(rtf,"+{\\footnote + BROWSE%04x:%04x}\n",BrowseNum);
  4295.                     if(LinkData2&&TopicLink.DataLen2>0)
  4296.                     {
  4297.                         char *q;
  4298.                         unsigned i;
  4299.  
  4300.                         if(TopicLink.DataLen2<=TopicLink.BlockSize-TopicLink.DataLen1)
  4301.                         {
  4302.                             end=LinkData2+TopicLink.DataLen2;
  4303.                         }
  4304.                         else
  4305.                         {
  4306.                             q=myMalloc(TopicLink.DataLen2+1);
  4307.                             end=StringPrint(LinkData2,TopicLink.BlockSize-TopicLink.DataLen1,q);
  4308.                             free(LinkData2);
  4309.                             LinkData2=q;
  4310.                             if(end>LinkData2+TopicLink.DataLen2)
  4311.                             {
  4312.                                 error("Phrase replacement delivers %u instead of %ld bytes\n",(unsigned int)(end-LinkData2),TopicLink.DataLen2);
  4313.                             }
  4314.                             while(end<LinkData2+TopicLink.DataLen2) *end++='\0';
  4315.                         }
  4316.                         *end='\0';
  4317.                         fprintf(rtf,"${\\footnote $ ");
  4318.                         putrtf(rtf,LinkData2);
  4319.                         fprintf(rtf,"}\n");
  4320.                         for(i=strlen(LinkData2)+1;i<TopicLink.DataLen2;i+=strlen(LinkData2+i)+1)
  4321.                         {
  4322.                             fprintf(rtf,"!{\\footnote ! ");
  4323.                             putrtf(rtf,LinkData2+i);
  4324.                             fprintf(rtf,"}\n");
  4325.                         }
  4326.                     }
  4327.                     ListWindows(HelpFile,rtf,pos);
  4328.                 }
  4329.             }
  4330.             else if(LinkData1&&LinkData2&&TopicLink.RecordType==TL_DISPLAY30||TopicLink.RecordType==TL_DISPLAY||TopicLink.RecordType==TL_TABLE)
  4331.             {
  4332.                 int col,cols,lastcol;
  4333.                 short *iptr;
  4334.                 unsigned short x1,x2,x3,x4;
  4335.                 short y1;
  4336.                 long l1;
  4337.                 char *ptr;
  4338.                 char *cmd;
  4339.                 char *end;
  4340.                 char *q;
  4341.                 unsigned int bits;
  4342.  
  4343.                 if(!doc&&TopicLink.RecordType!=TL_DISPLAY30&&counter!=offset)
  4344.                 {
  4345.                     if((offset&0x3FFF)!=0||counter>offset)
  4346.                     {
  4347.                         warnings=TRUE;
  4348.                         if(warn) fprintf(stderr,"\ncounter %08lx becomes %08lx\n",counter,offset);
  4349.                     }
  4350.                     counter=offset;
  4351.                 }
  4352.                 if(pos>=nonscroll) nonscroll=-1L;
  4353.                 ptr=scanlong(LinkData1,&l1);
  4354.                 switch(TopicLink.RecordType)
  4355.                 {
  4356.                 case TL_DISPLAY:
  4357.                     ptr=scanword(ptr,&x3);
  4358.                     if(!dontCount) TopicOffset+=x3;
  4359.                     break;
  4360.                 case TL_TABLE:
  4361.                     ptr=scanword(ptr,&x3);
  4362.                     if(!dontCount) TopicOffset+=x3;
  4363.                     fprintf(rtf,"\\trowd ");
  4364.                     cols=(unsigned char)*ptr++;
  4365.                     x4=(unsigned char)*ptr++;
  4366.                     switch(x4)
  4367.                     {
  4368.                     case 0:
  4369.                     case 2:
  4370.                         l1=*(short *)ptr; // min table width
  4371.                         ptr+=2;
  4372.                         fprintf(rtf,"\\trqc");
  4373.                         break;
  4374.                     case 1:
  4375.                         l1=32767L;
  4376.                         break;
  4377.                     case 3:
  4378.                         l1=3276L; // scaling is 1 instead of 10
  4379.                         break;
  4380.                     default:
  4381.                         error("\nunknown column data modifier %02x found\n",x4);
  4382.                     }
  4383.                     iptr=(short *)ptr;
  4384.                     if(cols>1)
  4385.                     {
  4386.                         x1=iptr[0]+iptr[1]+iptr[3]/2;
  4387.                         fprintf(rtf,"\\trgaph%ld\\trleft%ld \\cellx%ld\\cellx%ld",(iptr[3]*l1)/3276,((iptr[1]-iptr[3])*l1-32767)/3276,(x1*l1)/3276,((x1+iptr[2]+iptr[3])*l1)/3276);
  4388.                         x1+=iptr[2]+iptr[3];
  4389.                         for(col=2;col<cols;col++)
  4390.                         {
  4391.                             x1+=iptr[2*col]+iptr[2*col+1];
  4392.                             fprintf(rtf,"\\cellx%ld",(x1*l1)/3276);
  4393.                         }
  4394.                     }
  4395.                     else
  4396.                     {
  4397.                         fprintf(rtf,"\\trleft%ld \\cellx%ld ",(iptr[1]*l1-32767)/3276,(iptr[0]*l1)/3276);
  4398.                     }
  4399.                     ptr=(char *)(iptr+2*cols);
  4400.                     break;
  4401.                 }
  4402.                 // do phrase replacement of LinkData2
  4403.                 if(TopicLink.DataLen2<=TopicLink.BlockSize-TopicLink.DataLen1)
  4404.                 {
  4405.                     end=LinkData2+TopicLink.DataLen2;
  4406.                     q=LinkData2;
  4407.                 }
  4408.                 else
  4409.                 {
  4410.                     q=myMalloc(TopicLink.DataLen2+1);
  4411.                     end=StringPrint(LinkData2,TopicLink.BlockSize-TopicLink.DataLen1,q);
  4412.                     if(end>q+TopicLink.DataLen2)
  4413.                     {
  4414.                         error("phrase replacement delivers %u instead of %ld bytes\n",(unsigned int)(end-q),TopicLink.DataLen2);
  4415.                         HexDumpMemory(LinkData2,TopicLink.BlockSize-TopicLink.DataLen1);
  4416.                         StringPrint(LinkData2,TopicLink.BlockSize-TopicLink.DataLen1,NULL);
  4417.                         exit(1);
  4418.                     }
  4419.                     while(end<q+TopicLink.DataLen2) *end++='\0';
  4420.                     free(LinkData2);
  4421.                     LinkData2=q;
  4422.                 }
  4423.                 *end='\0';
  4424.                 lastcol=-1;
  4425.                 for(col=0;(TopicLink.RecordType==TL_TABLE?*(short *)ptr!=-1:col==0)&&ptr<LinkData1+TopicLink.DataLen1-sizeof(TOPICLINK);col++)
  4426.                 {
  4427.                     fprintf(rtf,"\\pard ");
  4428.                     if(nonscroll!=-1L) fprintf(rtf,"\\keepn ");
  4429.                     if(TopicLink.RecordType==TL_TABLE)
  4430.                     {
  4431.                         fprintf(rtf,"\\intbl ");
  4432.                         lastcol=*(short *)ptr;
  4433.                         ptr+=5;
  4434.                     }
  4435.                     ptr+=4;
  4436.                     bits=*(unsigned short *)ptr;
  4437.                     ptr+=2;
  4438.                     if(bits&0x0400) fprintf(rtf,"\\qr ");
  4439.                     if(bits&0x0800) fprintf(rtf,"\\qc ");
  4440.                     if(bits&0x0001)
  4441.                     {
  4442.                         ptr=scanlong(ptr,&l1);
  4443.                     }
  4444.                     if(bits&0x0002)
  4445.                     {
  4446.                         ptr=scanint(ptr,&y1);
  4447.                         fprintf(rtf,"\\sb%d ",y1*scaling);
  4448.                     }
  4449.                     if(bits&0x0004)
  4450.                     {
  4451.                         ptr=scanint(ptr,&y1);
  4452.                         fprintf(rtf,"\\sa%d ",y1*scaling);
  4453.                     }
  4454.                     if(bits&0x0008)
  4455.                     {
  4456.                         ptr=scanint(ptr,&y1);
  4457.                         fprintf(rtf,"\\sl%d ",y1*scaling);
  4458.                     }
  4459.                     if(bits&0x0010)
  4460.                     {
  4461.                         ptr=scanint(ptr,&y1);
  4462.                         fprintf(rtf,"\\li%d ",y1*scaling);
  4463.                     }
  4464.                     if(bits&0x0020)
  4465.                     {
  4466.                         ptr=scanint(ptr,&y1);
  4467.                         fprintf(rtf,"\\ri%d ",y1*scaling);
  4468.                     }
  4469.                     if(bits&0x0040)
  4470.                     {
  4471.                         ptr=scanint(ptr,&y1);
  4472.                         fprintf(rtf,"\\fi%d ",y1*scaling);
  4473.                     }
  4474.                     if(bits&0x0100)
  4475.                     {
  4476.                         x1=(unsigned char)*ptr++;
  4477.                         if(x1&1) fprintf(rtf,"\\box ");
  4478.                         if(x1&2) fprintf(rtf,"\\brdrt ");
  4479.                         if(x1&4) fprintf(rtf,"\\brdrl ");
  4480.                         if(x1&8) fprintf(rtf,"\\brdrb ");
  4481.                         if(x1&0x10) fprintf(rtf,"\\brdrr ");
  4482.                         if(x1&0x20) fprintf(rtf,"\\brdrth "); else fprintf(rtf,"\\brdrs ");
  4483.                         if(x1&0x40) fprintf(rtf,"\\brdrdb ");
  4484.                         ptr+=2;
  4485.                     }
  4486.                     if(bits&0x0200)
  4487.                     {
  4488.                         ptr=scanint(ptr,&y1);
  4489.                         while(y1-->0)
  4490.                         {
  4491.                             ptr=scanword(ptr,&x1);
  4492.                             if(x1&0x4000)
  4493.                             {
  4494.                                 ptr=scanword(ptr,&x2); // tab
  4495.                                 switch(x2)
  4496.                                 {
  4497.                                 case 1:
  4498.                                     fprintf(rtf,"\\tqr");
  4499.                                     break;
  4500.                                 case 2:
  4501.                                     fprintf(rtf,"\\tqc");
  4502.                                     break;
  4503.                                 }
  4504.                             }
  4505.                             fprintf(rtf,"\\tx%d ",(x1&0x3FFF)*scaling);
  4506.                         }
  4507.                     }
  4508.                     fprintf(rtf,"{");
  4509.                     while(1) // ptr<LinkData1+TopicLink.DataLen1-sizeof(TOPICLINK)&&q<end)
  4510.                     {
  4511.                         if(*q) if(fontset!=-1&&(font[fontset].Attributes&FONT_SMCP)) strlwr(q);
  4512.                         do
  4513.                         {
  4514.                             if(!doc)
  4515.                             {
  4516.                                 BOOL first;
  4517.  
  4518.                                 while(NextContextRec<ContextRecs&&ContextRec[NextContextRec].TopicOffset<counter)
  4519.                                 {
  4520.                                     warnings=TRUE;
  4521.                                     if(warn) fprintf(stderr,"\ncounter %08lx behind %08lx\n",counter,ContextRec[NextContextRec].TopicOffset);
  4522.                                     fprintf(rtf,"{#{\\footnote # %s}}\n",unhash(ContextRec[NextContextRec].HashValue));
  4523.                                     while(NextContextRec<ContextRecs&&ContextRec[NextContextRec].TopicOffset==ContextRec[NextContextRec+1].TopicOffset)
  4524.                                     {
  4525.                                         NextContextRec++;
  4526.                                     }
  4527.                                     NextContextRec++;
  4528.                                 }
  4529.                                 if(NextContextRec<ContextRecs&&ContextRec[NextContextRec].TopicOffset==counter)
  4530.                                 {
  4531.                                     fprintf(rtf,"{#{\\footnote # %s}}\n",unhash(ContextRec[NextContextRec].HashValue));
  4532.                                     while(NextContextRec<ContextRecs&&ContextRec[NextContextRec].TopicOffset==ContextRec[NextContextRec+1].TopicOffset)
  4533.                                     {
  4534.                                         NextContextRec++;
  4535.                                     }
  4536.                                     NextContextRec++;
  4537.                                 }
  4538.                                 first=TRUE;
  4539.                                 while(NextKeywordRec<KeywordRecs&&KeywordRec[NextKeywordRec].TopicOffset<counter)
  4540.                                 {
  4541. //                                    warnings=TRUE;
  4542. //                                    if(warn) fprintf(stderr,"\ncount %08lx behind %08lx\n",counter,KeywordRec[NextKeywordRec].TopicOffset);
  4543.                                     if(first)
  4544.                                     {
  4545.                                         fprintf(rtf,"%c{\\footnote %c ",KeywordRec[NextKeywordRec].Footnote,KeywordRec[NextKeywordRec].Footnote);
  4546.                                         first=FALSE;
  4547.                                     }
  4548.                                     else
  4549.                                     {
  4550.                                         fprintf(rtf,"; ");
  4551.                                     }
  4552.                                     putrtf(rtf,KeywordRec[NextKeywordRec].Keyword);
  4553.                                     NextKeywordRec++;
  4554.                                 }
  4555.                                 if(!first) fprintf(rtf,"}\n");
  4556.                                 first=TRUE;
  4557.                                 if(NextKeywordRec<KeywordRecs&&KeywordRec[NextKeywordRec].TopicOffset==counter)
  4558.                                 {
  4559.                                     if(first)
  4560.                                     {
  4561.                                         fprintf(rtf,"%c{\\footnote %c ",KeywordRec[NextKeywordRec].Footnote,KeywordRec[NextKeywordRec].Footnote);
  4562.                                         first=FALSE;
  4563.                                     }
  4564.                                     else
  4565.                                     {
  4566.                                         fprintf(rtf,"; ");
  4567.                                     }
  4568.                                     putrtf(rtf,KeywordRec[NextKeywordRec].Keyword);
  4569.                                     NextKeywordRec++;
  4570.                                 }
  4571.                                 if(!first) fprintf(rtf,"}\n");
  4572.                             }
  4573.                             if(*q!='{'&&*q!='}'&&*q!='\\'&&isprint((unsigned char)*q))
  4574.                             {
  4575.                                 putc(*q,rtf);
  4576.                             }
  4577.                             else if(!doc&&*q=='{')
  4578.                             {
  4579.                                 fprintf(rtf,"\\{\\-"); // emit invisible dash after { brace
  4580.                                 // because bmc or another legal command my follow, but this
  4581.                                 // command was not parsed the help file was build, so it was
  4582.                                 // used just as an example. The dash will be eaten up by the
  4583.                                 // help compiler on recompile.
  4584.                             }
  4585.                             else if(*q)
  4586.                             {
  4587.                                 fprintf(rtf,"\\'%02x",(unsigned char)*q);
  4588.                             }
  4589.                             counter++;
  4590.                         }
  4591.                         while(*q++);
  4592.                         if((unsigned char)ptr[0]==0xFF)
  4593.                         {
  4594.                             ptr++;
  4595.                             break;
  4596.                         }
  4597.                         else switch((unsigned char)ptr[0])
  4598.                         {
  4599.                         case 0x20: // vfld MVB
  4600.                             if(*(long *)(ptr+1))
  4601.                             {
  4602.                                 fprintf(rtf,"\\{vfld%ld\\}",*(long *)(ptr+1));
  4603.                             }
  4604.                             else
  4605.                             {
  4606.                                 fprintf(rtf,"\\{vfld\\}");
  4607.                             }
  4608.                             ptr+=5;
  4609.                             break;
  4610.                         case 0x21: // dtype MVB
  4611.                             if(*(short *)(ptr+1))
  4612.                             {
  4613.                                 fprintf(rtf,"\\{dtype%d\\}",*(short *)(ptr+1));
  4614.                             }
  4615.                             else
  4616.                             {
  4617.                                 fprintf(rtf,"\\{dtype\\}");
  4618.                             }
  4619.                             ptr+=3;
  4620.                             break;
  4621.                         case 0x80: // font change
  4622.                             ChangeFont(rtf,fontset=*(short *)(ptr+1),ul,uldb);
  4623.                             ptr+=3;
  4624.                             break;
  4625.                         case 0x81:
  4626.                             fprintf(rtf,"\\line\n");
  4627.                             ptr++;
  4628.                             break;
  4629.                         case 0x82:
  4630.                             if(TopicLink.RecordType==TL_TABLE)
  4631.                             {
  4632.                                 if((unsigned char)ptr[1]!=0xFF)
  4633.                                 {
  4634.                                     fprintf(rtf,"\n\\par \\intbl ");
  4635.                                 }
  4636.                                 else if(*(short *)(ptr+2)==-1)
  4637.                                 {
  4638.                                     fprintf(rtf,"\\cell \\intbl \\row\n");
  4639.                                 }
  4640.                                 else if(*(short *)(ptr+2)==lastcol)
  4641.                                 {
  4642.                                     fprintf(rtf,"\\par \\pard ");
  4643.                                 }
  4644.                                 else
  4645.                                 {
  4646.                                     fprintf(rtf,"\\cell \\pard ");
  4647.                                 }
  4648.                             }
  4649.                             else
  4650.                             {
  4651.                                 fprintf(rtf,"\n\\par ");
  4652.                             }
  4653.                             ptr++;
  4654.                             break;
  4655.                         case 0x83:
  4656.                             fprintf(rtf,"\\tab ");
  4657.                             ptr++;
  4658.                             break;
  4659.                         case 0x86:
  4660.                             x3=(unsigned char)*ptr++;
  4661.                             x1=*ptr++;
  4662.                             if(x1==0x05) cmd="ewc"; else cmd="bmc";
  4663.                             goto picture;
  4664.                         case 0x87:
  4665.                             x3=(unsigned char)*ptr++;
  4666.                             x1=*ptr++;
  4667.                             if(x1==0x05) cmd="ewl"; else cmd="bml";
  4668.                             goto picture;
  4669.                         case 0x88:
  4670.                             x3=(unsigned char)*ptr++;
  4671.                             x1=*ptr++;
  4672.                             if(x1==0x05) cmd="ewr"; else cmd="bmr";
  4673.                             goto picture;
  4674.                         picture:
  4675.                             ptr=scanlong(ptr,&l1);
  4676.                             switch(x1)
  4677.                             {
  4678.                             case 0x22: // HC31
  4679.                                 ptr=scanword(ptr,&x1);
  4680.                                 counter+=x1; // number of hotspots in picture added
  4681.                                 // because value of counter is used inside WinHelp
  4682.                                 // to record actual position (selected hotspot).
  4683.                                 // fall thru
  4684.                             case 0x03: // HC30
  4685.                                 x1=((unsigned short *)ptr)[0];
  4686.                                 switch(x1)
  4687.                                 {
  4688.                                 case 1:
  4689.                                     while(nextbitmap<extensions&&extension[nextbitmap]<0x10) nextbitmap++;
  4690.                                     if(nextbitmap>=extensions)
  4691.                                     {
  4692.                                         error("Bitmap never saved\n");
  4693.                                         break;
  4694.                                     }
  4695.                                     x2=nextbitmap++;
  4696.                                     goto other;
  4697.                                 case 0:
  4698.                                     x2=((unsigned short *)ptr)[1];
  4699.                                 other:
  4700.                                     if(doc)
  4701.                                     {
  4702.                                         switch(x3)
  4703.                                         {
  4704.                                         case 0x86:
  4705.                                             fprintf(rtf,"{\\field {\\*\\fldinst import %s \\* Mergeformat}}",getbitmapname(x2));
  4706.                                             break;
  4707.                                         case 0x87:
  4708.                                             fprintf(rtf,"{\\pvpara {\\field {\\*\\fldinst import %s \\* Mergeformat}}\\par}\n",getbitmapname(x2));
  4709.                                             break;
  4710.                                         case 0x88:
  4711.                                             fprintf(rtf,"{\\pvpara\\posxr{\\field {\\*\\fldinst import %s \\* Mergeformat}}\\par}\n",getbitmapname(x2));
  4712.                                             break;
  4713.                                         }
  4714.                                     }
  4715.                                     else
  4716.                                     {
  4717.                                         if(x2<extensions&&(extension[x2]&0x0F)==5)
  4718.                                         {
  4719.                                             if(strcmp(cmd,"bmc")==0) cmd="bmct";
  4720.                                             else if(strcmp(cmd,"bml")==0) cmd="bmlt";
  4721.                                             else if(strcmp(cmd,"bmr")==0) cmd="bmrt";
  4722.                                         }
  4723.                                         fprintf(rtf,"\\{%s %s\\}",cmd,getbitmapname(x2));
  4724.                                     }
  4725.                                     break;
  4726.                                 }
  4727.                                 break;
  4728.                             case 0x05: // ewc,ewl,ewr
  4729.                                 if(ptr[6]=='!')
  4730.                                 {
  4731.                                     fprintf(rtf,"\\{button %s\\}",ptr+7);
  4732.                                 }
  4733.                                 else if(ptr[6]=='*')
  4734.                                 {
  4735.                                     char *plus;
  4736.                                     int n,c1,c2;
  4737.  
  4738.                                     sscanf(ptr+7,"%d,%d,%n",&c1,&c2,&n);
  4739.                                     plus=strchr(ptr+7+n,'+');
  4740.                                     if((c1&0xFFF5)!=0x8400) fprintf(stderr,"mci c1=%04x\n",c1);
  4741.                                     fprintf(rtf,"\\{mci");
  4742.                                     if(cmd[2]=='r') fprintf(rtf,"_right");
  4743.                                     if(cmd[2]=='l') fprintf(rtf,"_left");
  4744.                                     if(c2==1) fprintf(rtf," REPEAT");
  4745.                                     if(c2==2) fprintf(rtf," PLAY");
  4746.                                     if(!plus) fprintf(rtf," EXTERNAL");
  4747.                                     if(c1&8) fprintf(rtf," NOMENU");
  4748.                                     if(c1&2) fprintf(rtf," NOPLAYBAR");
  4749.                                     fprintf(rtf,",%s\\}\n",plus?plus+1:ptr+7+n);
  4750.                                 }
  4751.                                 else
  4752.                                 {
  4753.                                     fprintf(rtf,"\\{%s %s\\}",cmd,ptr+6);
  4754.                                 }
  4755.                                 break;
  4756.                             default:
  4757.                                 error("Unknown picture flags %02x\n",x1);
  4758.                             }
  4759.                             ptr+=l1;
  4760.                             break;
  4761.                         case 0x89: // end of hotspot
  4762.                             ChangeFont(rtf,fontset,ul=FALSE,uldb=FALSE);
  4763.                             fprintf(rtf,"{\\v %s}",hotspot);
  4764.                             ptr++;
  4765.                             break;
  4766.                         case 0xC8: // macro
  4767.                             ChangeFont(rtf,fontset,FALSE,uldb=TRUE);
  4768.                             hotspot=myReAlloc(hotspot,strlen(ptr+3)+2);
  4769.                             sprintf(hotspot,"!%s",ptr+3);
  4770.                             ptr+=*(short *)(ptr+1)+3;
  4771.                             break;
  4772.                         case 0xCC: // macro without font change
  4773.                             ChangeFont(rtf,fontset,FALSE,uldb=TRUE);
  4774.                             hotspot=myReAlloc(hotspot,strlen(ptr+3)+3);
  4775.                             sprintf(hotspot,"*!%s",ptr+3);
  4776.                             ptr+=*(short *)(ptr+1)+3;
  4777.                             break;
  4778.                         case 0xE0: // popup jump HC30
  4779.                             ChangeFont(rtf,fontset,ul=TRUE,FALSE);
  4780.                             goto label0;
  4781.                         case 0xE1: // topic jump HC30
  4782.                             ChangeFont(rtf,fontset,FALSE,uldb=TRUE);
  4783.                         label0:
  4784.                             hotspot=myReAlloc(hotspot,128);
  4785.                             sprintf(hotspot,"TOPIC%ld",*(long *)(ptr+1));
  4786.                             ptr+=5;
  4787.                             break;
  4788.                         case 0xE2: // popup jump HC31
  4789.                             ChangeFont(rtf,fontset,ul=TRUE,FALSE);
  4790.                             goto label1;
  4791.                         case 0xE3: // topic jump HC31
  4792.                             ChangeFont(rtf,fontset,FALSE,uldb=TRUE);
  4793.                         label1:
  4794.                             arg=unhash(*(long *)(ptr+1));
  4795.                             hotspot=myReAlloc(hotspot,strlen(arg)+1);
  4796.                             sprintf(hotspot,"%s",arg);
  4797.                             ptr+=5;
  4798.                             break;
  4799.                         case 0xE6: // popup jump without font change
  4800.                             ChangeFont(rtf,fontset,ul=TRUE,FALSE);
  4801.                             goto label2;
  4802.                         case 0xE7: // topic jump without font change
  4803.                             ChangeFont(rtf,fontset,FALSE,uldb=TRUE);
  4804.                         label2:
  4805.                             arg=unhash(*(long *)(ptr+1));
  4806.                             hotspot=myReAlloc(hotspot,strlen(arg)+2);
  4807.                             sprintf(hotspot,"*%s",arg);
  4808.                             ptr+=5;
  4809.                             break;
  4810.                         case 0xEA: // popup jump into external file
  4811.                         case 0xEE:
  4812.                             ChangeFont(rtf,fontset,ul=TRUE,FALSE);
  4813.                             goto label3;
  4814.                         case 0xEB: // topic jump into external file / secondary window
  4815.                         case 0xEF:
  4816.                             ChangeFont(rtf,fontset,FALSE,uldb=TRUE);
  4817.                         label3:
  4818.                             if((unsigned char)ptr[0]==0xEE||(unsigned char)ptr[0]==0xEF)
  4819.                             {
  4820.                                 cmd="*";
  4821.                             }
  4822.                             else
  4823.                             {
  4824.                                 cmd="";
  4825.                             }
  4826.                             arg=unhash(*(long *)(ptr+4));
  4827.                             switch((unsigned char)ptr[3])
  4828.                             {
  4829.                             case 0:
  4830.                                 hotspot=myReAlloc(hotspot,strlen(cmd)+strlen(arg)+1);
  4831.                                 sprintf(hotspot,"%s%s",cmd,arg);
  4832.                                 break;
  4833.                             case 1:
  4834.                                 hotspot=myReAlloc(hotspot,strlen(cmd)+strlen(arg)+1+strlen(WindowName(ptr[8]))+1);
  4835.                                 sprintf(hotspot,"%s%s>%s",cmd,arg,WindowName(ptr[8]));
  4836.                                 break;
  4837.                             case 4:
  4838.                                 hotspot=myReAlloc(hotspot,strlen(cmd)+strlen(arg)+1+strlen(ptr+8)+1);
  4839.                                 sprintf(hotspot,"%s%s@%s",cmd,arg,ptr+8);
  4840.                                 break;
  4841.                             case 6:
  4842.                                 hotspot=myReAlloc(hotspot,strlen(cmd)+strlen(arg)+1+strlen(ptr+8)+1+strlen(strchr(ptr+8,'\0')+1)+1);
  4843.                                 sprintf(hotspot,"%s%s>%s@%s",cmd,arg,ptr+8,strchr(ptr+8,'\0')+1);
  4844.                                 break;
  4845.                             default:
  4846.                                 error("Unknown modifier %02x in tag %02x\n",(unsigned char)ptr[3],(unsigned char)ptr[0]);
  4847.                             }
  4848.                             ptr+=*(short *)(ptr+1)+3;
  4849.                             break;
  4850.                         case 0x8B: // unknown, found in BACKSDK.MVB
  4851.                         case 0x8C: // unknown, found in BACKSDK.MVB
  4852.                         default:
  4853.                             fprintf(stderr,"\n%02x unknown\n",(unsigned char)ptr[0]);
  4854.                             ptr++;
  4855.                         }
  4856.                     }
  4857.                     fprintf(rtf,"}");
  4858.                 }
  4859.             }
  4860.             else
  4861.             {
  4862.                 if(LinkData1)
  4863.                 {
  4864.                     printf("LinkData1:");
  4865.                     for(i=0;i<TopicLink.DataLen1-sizeof(TOPICLINK);i++)
  4866.                     {
  4867.                          if(i%16==0) printf("\n%04x",i);
  4868.                          printf(" %02X",(unsigned char)LinkData1[i]);
  4869.                     }
  4870.                     if(i%16) printf("\n");
  4871.                 }
  4872.                 if(LinkData2)
  4873.                 {
  4874.                     printf("LinkData2:");
  4875.                     for(i=0;i<TopicLink.DataLen2;i++)
  4876.                     {
  4877.                          if(i%16==0) printf("\n%04x",i);
  4878.                          printf(" %02X",(unsigned char)LinkData2[i]);
  4879.                     }
  4880.                     if(i%16) printf("\n");
  4881.                 }
  4882.             }
  4883.             if(LinkData1) free(LinkData1);
  4884.             if(LinkData2) free(LinkData2);
  4885.             dontCount=FALSE;
  4886.             memcpy(&TopicLink,&NextTopicLink,sizeof(TopicLink));
  4887.         }
  4888.     }
  4889. }
  4890.  
  4891. void TTLDump(FILE *HelpFile,long FileStart)
  4892. {
  4893.     char Title[256];
  4894.     int entries,count;
  4895.     long TopicOffset;
  4896.     BUFFER buf;
  4897.  
  4898.     entries=GetFirstPage(HelpFile,FileStart,&buf);
  4899.     while(entries)
  4900.     {
  4901.         for(count=1;count<=entries;count++)
  4902.         {
  4903.             myFRead(&TopicOffset,sizeof(TopicOffset),HelpFile);
  4904.             StringRead(Title,sizeof(Title),HelpFile);
  4905.             printf("Topic Offset:0x%08lX  Title: %s\n",TopicOffset,Title);
  4906.         }
  4907.         entries=GetNextPage(HelpFile,&buf);
  4908.     }
  4909. }
  4910.  
  4911. int ContextRecCmp(const void *a,const void *b)
  4912. {
  4913.     const CONTEXTREC *A;
  4914.     const CONTEXTREC *B;
  4915.  
  4916.     A=(const CONTEXTREC *)a;
  4917.     B=(const CONTEXTREC *)b;
  4918.     if(A->TopicOffset<B->TopicOffset) return -1;
  4919.     if(A->TopicOffset>B->TopicOffset) return 1;
  4920.     return 0;
  4921. }
  4922.  
  4923. void ContextLoad(FILE *HelpFile)
  4924. {
  4925.     long FileStart;
  4926.     BUFFER buf;
  4927.     int entries;
  4928.  
  4929.     if(SearchFile(HelpFile,"|CONTEXT",&FileStart))
  4930.     {
  4931.         entries=GetFirstPage(HelpFile,FileStart,&buf);
  4932.         while(entries)
  4933.         {
  4934.             ContextRec=myReAlloc(ContextRec,(long)(ContextRecs+entries)*sizeof(CONTEXTREC));
  4935.             myFRead(ContextRec+ContextRecs,entries*sizeof(CONTEXTREC),HelpFile);
  4936.             ContextRecs+=entries;
  4937.             entries=GetNextPage(HelpFile,&buf);
  4938.         }
  4939.         qsort(ContextRec,ContextRecs,sizeof(CONTEXTREC),ContextRecCmp);
  4940.         printf("%d topic offsets and hash values loaded\n",ContextRecs);
  4941.     }
  4942. }
  4943.  
  4944. void GenerateContent(FILE *HelpFile,FILE *ContentFile)
  4945. {
  4946.     typedef struct
  4947.     {
  4948.         long TopicOffset;
  4949.         long WindowNumber;
  4950.     }
  4951.     WINDOWREC;
  4952.     WINDOWREC *WindowRec;
  4953.     int entries,count,WindowRecs;
  4954.     BUFFER buf;
  4955.     long FileStart,CurrentLocation,lastpos,TopicOffset;
  4956.     FILEHEADER FileHdr;
  4957.     SYSTEMREC SystemRec;
  4958.     SECWINDOW *SWin;
  4959.     int i,windows;
  4960.     char *data;
  4961.  
  4962.     fprintf(ContentFile,":Base %s%s>main\n",name,ext);
  4963.     if(SearchFile(HelpFile,"|SYSTEM",&FileStart))
  4964.     {
  4965.         fseek(HelpFile,FileStart,SEEK_SET);
  4966.         myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  4967.         myFRead(&SysHeader,sizeof(SysHeader),HelpFile);
  4968.         if(before31)
  4969.         {
  4970.             myFRead(buffer,FileHdr.FileSize-sizeof(SysHeader),HelpFile);
  4971.             fprintf(ContentFile,":Title %s\n",buffer);
  4972.         }
  4973.         else
  4974.         {
  4975.             CurrentLocation=12;
  4976.             lastpos=ftell(HelpFile);
  4977.             SWin=NULL;
  4978.             windows=0;
  4979.             while(CurrentLocation<FileHdr.FileSize)
  4980.             {
  4981.                 myFRead(&SystemRec,sizeof(SYSTEMREC),HelpFile);
  4982.                 data=myMalloc(SystemRec.DataSize+1);
  4983.                 myFRead(data,SystemRec.DataSize,HelpFile);
  4984.                 CurrentLocation=CurrentLocation+4+SystemRec.DataSize;
  4985.                 data[SystemRec.DataSize]='\0';
  4986.                 switch(SystemRec.RecordType)
  4987.                 {
  4988.                 case 0x0001:
  4989.                     strcpy(buffer,data);
  4990.                     fprintf(ContentFile,":Title %s\n",buffer);
  4991.                     break;
  4992.                 case 0x0004:
  4993.                     AddMacro(data);
  4994.                     break;
  4995.                 case 0x0006:
  4996.                     windows++;
  4997.                     break;
  4998.                 }
  4999.                 free(data);
  5000.             }
  5001.             if(windows)
  5002.             {
  5003.                 secondarywindownames=myMalloc((windows+1)*sizeof(char *));
  5004.                 for(i=0;i<=windows;i++) secondarywindownames[i]=NULL;
  5005.                 fseek(HelpFile,lastpos,SEEK_SET);
  5006.                 CurrentLocation=12;
  5007.                 i=0;
  5008.                 while(CurrentLocation<FileHdr.FileSize)
  5009.                 {
  5010.                     myFRead(&SystemRec,sizeof(SYSTEMREC),HelpFile);
  5011.                     data=myMalloc(SystemRec.DataSize+1);
  5012.                     myFRead(data,SystemRec.DataSize,HelpFile);
  5013.                     data[SystemRec.DataSize]='\0';
  5014.                     CurrentLocation=CurrentLocation+4+SystemRec.DataSize;
  5015.                     switch(SystemRec.RecordType)
  5016.                     {
  5017.                     case 0x0006:
  5018.                         SWin=(SECWINDOW *)data;
  5019.                         if(SWin->Flags&WSYSFLAG_NAME)
  5020.                         {
  5021.                             secondarywindownames[i]=myStrDup(SWin->Name);
  5022.                         }
  5023.                         i++;
  5024.                         break;
  5025.                     }
  5026.                     free(data);
  5027.                 }
  5028.             }
  5029.         }
  5030.     }
  5031.     ContextLoad(HelpFile);
  5032.     if(SearchFile(HelpFile,"|TopicId",&FileStart))
  5033.     {
  5034.         int entries,count;
  5035.         long offset;
  5036.         BUFFER buf;
  5037.  
  5038.         entries=GetFirstPage(HelpFile,FileStart,&buf);
  5039.         while(entries)
  5040.         {
  5041.             for(count=1;count<=entries;count++)
  5042.             {
  5043.                 myFRead(&offset,sizeof(offset),HelpFile);
  5044.                 StringRead(buffer,sizeof(buffer),HelpFile);
  5045.                 AddTopic(buffer);
  5046.             }
  5047.             entries=GetNextPage(HelpFile,&buf);
  5048.         }
  5049.     }
  5050.     WindowRec=NULL;
  5051.     WindowRecs=0;
  5052.     if(SearchFile(HelpFile,"|Petra",&FileStart))
  5053.     {
  5054.         entries=GetFirstPage(HelpFile,FileStart,&buf);
  5055.         while(entries)
  5056.         {
  5057.             WindowRec=myReAlloc(WindowRec,(long)(WindowRecs+entries)*sizeof(WINDOWREC));
  5058.             myFRead(WindowRec+WindowRecs,entries*sizeof(WINDOWREC),HelpFile);
  5059.             WindowRecs+=entries;
  5060.             entries=GetNextPage(HelpFile,&buf);
  5061.         }
  5062.     }
  5063.     if(SearchFile(HelpFile,"|TTLBTREE",&FileStart))
  5064.     {
  5065.         entries=GetFirstPage(HelpFile,FileStart,&buf);
  5066.         while(entries)
  5067.         {
  5068.             for(count=1;count<=entries;count++)
  5069.             {
  5070.                 myFRead(&TopicOffset,sizeof(TopicOffset),HelpFile);
  5071.                 if(StringRead(buffer,sizeof(buffer),HelpFile))
  5072.                 {
  5073.                     char *ptr;
  5074.  
  5075.                     ptr=TopicName(TopicOffset,FALSE);
  5076.                     if(ptr)
  5077.                     {
  5078.                         fprintf(ContentFile,"1 %s=%s",buffer,ptr);
  5079.                         for(i=0;i<WindowRecs;i++)
  5080.                         {
  5081.                             if(WindowRec[i].TopicOffset==TopicOffset)
  5082.                             {
  5083.                                 fprintf(ContentFile,">%s",WindowName(WindowRec[i].WindowNumber));
  5084.                                 break;
  5085.                             }
  5086.                         }
  5087.                         fprintf(ContentFile,"\n");
  5088.                     }
  5089.                     else
  5090.                     {
  5091.                         fprintf(stderr,"%s\n",buffer);
  5092.                     }
  5093.                 }
  5094.             }
  5095.             entries=GetNextPage(HelpFile,&buf);
  5096.         }
  5097.     }
  5098. }
  5099.  
  5100. void RoseDump(FILE *HelpFile,long FileStart)
  5101. {
  5102.     char buffer[1024];
  5103.     int entries,count,len;
  5104.     long offset;
  5105.     BUFFER buf;
  5106.  
  5107.     entries=GetFirstPage(HelpFile,FileStart,&buf);
  5108.     while(entries)
  5109.     {
  5110.         for(count=1;count<=entries;count++)
  5111.         {
  5112.             myFRead(&offset,sizeof(offset),HelpFile);
  5113.             len=StringRead(buffer,sizeof(buffer),HelpFile);
  5114.             StringRead(buffer+len+1,sizeof(buffer)-len-1,HelpFile);
  5115.             printf("0x%08lX: %s,%s\n",offset,buffer,buffer+len+1);
  5116.         }
  5117.         entries=GetNextPage(HelpFile,&buf);
  5118.     }
  5119. }
  5120.  
  5121. void TopicIdDump(FILE *HelpFile,long FileStart)
  5122. {
  5123.     char buffer[1024];
  5124.     int entries,count;
  5125.     long offset;
  5126.     BUFFER buf;
  5127.  
  5128.     entries=GetFirstPage(HelpFile,FileStart,&buf);
  5129.     while(entries)
  5130.     {
  5131.         for(count=1;count<=entries;count++)
  5132.         {
  5133.             myFRead(&offset,sizeof(offset),HelpFile);
  5134.             StringRead(buffer,sizeof(buffer),HelpFile);
  5135.             printf("0x%08lX: %s\n",offset,buffer);
  5136.         }
  5137.         entries=GetNextPage(HelpFile,&buf);
  5138.     }
  5139. }
  5140.  
  5141. void PetraDump(FILE *HelpFile,long FileStart)
  5142. {
  5143.     char buffer[1024];
  5144.     int entries,count;
  5145.     long offset;
  5146.     BUFFER buf;
  5147.  
  5148.     entries=GetFirstPage(HelpFile,FileStart,&buf);
  5149.     while(entries)
  5150.     {
  5151.         for(count=1;count<=entries;count++)
  5152.         {
  5153.             myFRead(&offset,sizeof(offset),HelpFile);
  5154.             StringRead(buffer,sizeof(buffer),HelpFile);
  5155.             printf("0x%08lX: %s\n",offset,buffer);
  5156.         }
  5157.         entries=GetNextPage(HelpFile,&buf);
  5158.     }
  5159. }
  5160.  
  5161. void VIOLADump(FILE *HelpFile,long FileStart)
  5162. {
  5163.     int entries,count;
  5164.     long offset,value;
  5165.     BUFFER buf;
  5166.     long SystemStart,CurrentLocation,lastpos;
  5167.     FILEHEADER FileHdr;
  5168.     SYSTEMREC SystemRec;
  5169.     SECWINDOW *SWin;
  5170.     int i,windows;
  5171.     char *data;
  5172.  
  5173.     if(SearchFile(HelpFile,"|SYSTEM",&SystemStart))
  5174.     {
  5175.         fseek(HelpFile,SystemStart,SEEK_SET);
  5176.         myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  5177.         myFRead(&SysHeader,sizeof(SysHeader),HelpFile);
  5178.         if(!before31)
  5179.         {
  5180.             CurrentLocation=12;
  5181.             lastpos=ftell(HelpFile);
  5182.             SWin=NULL;
  5183.             windows=0;
  5184.             while(CurrentLocation<FileHdr.FileSize)
  5185.             {
  5186.                 myFRead(&SystemRec,sizeof(SYSTEMREC),HelpFile);
  5187.                 CurrentLocation+=4;
  5188.                 if(SystemRec.DataSize)
  5189.                 {
  5190.                     data=myMalloc(SystemRec.DataSize+1);
  5191.                     myFRead(data,SystemRec.DataSize,HelpFile);
  5192.                     data[SystemRec.DataSize]='\0';
  5193.                     switch(SystemRec.RecordType)
  5194.                     {
  5195.                     case 0x0006:
  5196.                         windows++;
  5197.                     }
  5198.                     free(data);
  5199.                 }
  5200.             }
  5201.             if(windows)
  5202.             {
  5203.                 secondarywindownames=myMalloc((windows+1)*sizeof(char *));
  5204.                 for(i=0;i<=windows;i++) secondarywindownames[i]=NULL;
  5205.                 fseek(HelpFile,lastpos,SEEK_SET);
  5206.                 CurrentLocation=12;
  5207.                 i=0;
  5208.                 while(CurrentLocation<FileHdr.FileSize)
  5209.                 {
  5210.                     myFRead(&SystemRec,sizeof(SYSTEMREC),HelpFile);
  5211.                     data=myMalloc(SystemRec.DataSize+1);
  5212.                     myFRead(data,SystemRec.DataSize,HelpFile);
  5213.                     data[SystemRec.DataSize]='\0';
  5214.                     CurrentLocation=CurrentLocation+4+SystemRec.DataSize;
  5215.                     switch(SystemRec.RecordType)
  5216.                     {
  5217.                     case 0x0006:
  5218.                         SWin=(SECWINDOW *)data;
  5219.                         if(SWin->Flags&WSYSFLAG_NAME)
  5220.                         {
  5221.                             secondarywindownames[i]=myStrDup(SWin->Name);
  5222.                         }
  5223.                         i++;
  5224.                         break;
  5225.                     }
  5226.                     free(data);
  5227.                 }
  5228.             }
  5229.         }
  5230.     }
  5231.     entries=GetFirstPage(HelpFile,FileStart,&buf);
  5232.     while(entries)
  5233.     {
  5234.         for(count=1;count<=entries;count++)
  5235.         {
  5236.             myFRead(&offset,sizeof(offset),HelpFile);
  5237.             myFRead(&value,sizeof(value),HelpFile);
  5238.             printf("Topic Offset: 0x%08lX Window: %s\n",offset,WindowName(value));
  5239.         }
  5240.         entries=GetNextPage(HelpFile,&buf);
  5241.     }
  5242. }
  5243.  
  5244. void KWMapDump(FILE *HelpFile,long FileStart)
  5245. {
  5246.     FILEHEADER   FileHdr;
  5247.     unsigned int NumKWMaps,count;
  5248.     KWMAPREC     KeywordMap;
  5249.  
  5250.     fseek(HelpFile,FileStart,SEEK_SET);
  5251.     myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  5252.     myFRead(&NumKWMaps,sizeof(NumKWMaps),HelpFile);
  5253.     for(count=1;count<=NumKWMaps;count++)
  5254.     {
  5255.         myFRead(&KeywordMap,sizeof(KWMAPREC),HelpFile);
  5256.         printf("Record:%05u   First Keyword:0x%08lX     Leaf Page#:%05u\n",count,KeywordMap.FirstRec,KeywordMap.PageNum);
  5257.     }
  5258. }
  5259.  
  5260. void KWDataDump(FILE *HelpFile,long FileStart)
  5261. {
  5262.     FILEHEADER FileHdr;
  5263.     unsigned short NumKWLocs,count;
  5264.     long TopicOffset;
  5265.  
  5266.     fseek(HelpFile,FileStart,SEEK_SET);
  5267.     myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  5268.     NumKWLocs=(unsigned short)(FileHdr.FileSize/4);
  5269.     printf("Number of Keyword Occurrances: %5u\n\n",NumKWLocs);
  5270.     for(count=1;count<=NumKWLocs;count++)
  5271.     {
  5272.         myFRead(&TopicOffset,sizeof(TopicOffset),HelpFile);
  5273.         printf("Occurance:%05u   Topic Offset:0x%08lX\n",count,TopicOffset);
  5274.     }
  5275. }
  5276.  
  5277. void KWBTreeDump(FILE *HelpFile,long FileStart)
  5278. {
  5279.     int entries,i;
  5280.     char Keyword[256];  // Variable Length Keyword
  5281.     short Count;       // Count of Keywords occurances
  5282.     long KWDataOffset; // Offset into |KWDATA file
  5283.     BUFFER buf;
  5284.  
  5285.     entries=GetFirstPage(HelpFile,FileStart,&buf);
  5286.     while(entries)
  5287.     {
  5288.         for(i=1;i<=entries;i++)
  5289.         {
  5290.             StringRead(Keyword,sizeof(Keyword),HelpFile);
  5291.             myFRead(&Count,sizeof(Count),HelpFile);
  5292.             myFRead(&KWDataOffset,sizeof(KWDataOffset),HelpFile);
  5293.             printf("KWData Offset:0x%08lX    # Offsets:%05d    Keyword: %s\n",KWDataOffset,Count,Keyword);
  5294.         }
  5295.         entries=GetNextPage(HelpFile,&buf);
  5296.     }
  5297. }
  5298.  
  5299. void CatalogDump(FILE *HelpFile,long FileStart)
  5300. {
  5301.     FILEHEADER FileHdr;
  5302.     struct
  5303.     {
  5304.         unsigned short magic; // 0x1111
  5305.         unsigned short always8;
  5306.         unsigned short always4;
  5307.         long entries;
  5308.         unsigned char zero[30];
  5309.     }
  5310.     catalog;
  5311.     long count;
  5312.     long TopicOffset;
  5313.     int i;
  5314.  
  5315.     fseek(HelpFile,FileStart,SEEK_SET);
  5316.     myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  5317.     myFRead(&catalog,sizeof(catalog),HelpFile);
  5318.     printf("|CATALOG Dump: 0x%04x 0x%04x 0x%04x %ld\n",catalog.magic,catalog.always8,catalog.always4,catalog.entries);
  5319.     for(i=0;i<16;i++) printf("%02x ",catalog.zero[i]);
  5320.     printf("\n");
  5321.     for(i=16;i<30;i++) printf("%02x ",catalog.zero[i]);
  5322.     printf("\nTopic Offsets:\n");
  5323.     for(count=0;count<catalog.entries;count++)
  5324.     {
  5325.         myFRead(&TopicOffset,sizeof(TopicOffset),HelpFile);
  5326.         printf("%08lx %c",TopicOffset,count%8==7?'\n':' ');
  5327.     }
  5328.     if(count%8) printf("\n");
  5329. }
  5330.  
  5331. void ContextDump(FILE *HelpFile,long FileStart)
  5332. {
  5333.     int i,entries;
  5334.     CONTEXTREC ContextRec;
  5335.     BUFFER buf;
  5336.  
  5337.     entries=GetFirstPage(HelpFile,FileStart,&buf);
  5338.     while(entries)
  5339.     {
  5340.         for(i=0;i<entries;i++)
  5341.         {
  5342.             myFRead(&ContextRec,sizeof(ContextRec),HelpFile);
  5343.             printf("hash value: 0x%08lx   string: %-20s  topic offset: 0x%08lX\n",ContextRec.HashValue,unhash(ContextRec.HashValue),ContextRec.TopicOffset);
  5344.         }
  5345.         entries=GetNextPage(HelpFile,&buf);
  5346.     }
  5347. }
  5348.  
  5349. void AliasList(FILE *hpj)
  5350. {
  5351.     int i,n;
  5352.     BOOL headerwritten;
  5353.  
  5354.     if(ContextRec)
  5355.     {
  5356.         headerwritten=FALSE;
  5357.         for(i=0;i<ContextRecs;i=n)
  5358.         {
  5359.             for(n=i+1;n<ContextRecs&&ContextRec[i].TopicOffset==ContextRec[n].TopicOffset;n++)
  5360.             {
  5361.                 if(!headerwritten)
  5362.                 {
  5363.                     fprintf(stderr,"Creating [ALIAS] section...\n");
  5364.                     fprintf(hpj,"[ALIAS]\n");
  5365.                     headerwritten=TRUE;
  5366.                 }
  5367.                 fprintf(hpj,"%s=",unhash(ContextRec[n].HashValue));
  5368.                 fprintf(hpj,"%s\n",unhash(ContextRec[i].HashValue));
  5369.             }
  5370.         }
  5371.         if(headerwritten) fprintf(hpj,"\n");
  5372.     }
  5373. }
  5374.  
  5375. void CTXOMAPDump(FILE *HelpFile,long FileStart)
  5376. {
  5377.     FILEHEADER      FileHdr;
  5378.     CTXOMAPREC      CTXORec;
  5379.     unsigned short  NumRecs,i;
  5380.  
  5381.     fseek(HelpFile,FileStart,SEEK_SET);
  5382.     myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  5383.     myFRead(&NumRecs,sizeof(NumRecs),HelpFile);
  5384.     printf("Number of Context Mapping records: %u\n\n",NumRecs);
  5385.     for(i=1;i<=NumRecs;i++)
  5386.     {
  5387.         myFRead(&CTXORec,sizeof(CTXORec),HelpFile);
  5388.         printf("record:%05u    map ID:0x%08lX     topic offset:0x%08lX\n",i,CTXORec.MapID,CTXORec.TopicOffset);
  5389.     }
  5390. }
  5391.  
  5392. void CTXOMAPList(FILE *HelpFile,FILE *hpj)
  5393. {
  5394.     FILEHEADER      FileHdr;
  5395.     CTXOMAPREC      CTXORec;
  5396.     unsigned short  NumRecs,i;
  5397.     long            FileStart;
  5398.  
  5399.     if(hpj&&SearchFile(HelpFile,"|CTXOMAP",&FileStart))
  5400.     {
  5401.         fseek(HelpFile,FileStart,SEEK_SET);
  5402.         myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  5403.         myFRead(&NumRecs,sizeof(NumRecs),HelpFile);
  5404.         if(NumRecs)
  5405.         {
  5406.             fprintf(stderr,"Creating [MAP] section...\n");
  5407.             fprintf(hpj,"[MAP]\n");
  5408.             for(i=0;i<NumRecs;i++)
  5409.             {
  5410.                 myFRead(&CTXORec,sizeof(CTXORec),HelpFile);
  5411.                 fprintf(hpj,"%s %ld\n",TopicName(CTXORec.TopicOffset,TRUE),CTXORec.MapID);
  5412.             }
  5413.             fprintf(hpj,"\n");
  5414.         }
  5415.     }
  5416. }
  5417.  
  5418. // 1. extract topic names from topic macros, embedded pictures, and hotspot macros
  5419. // 2. build browse sequence start list
  5420. // 3. extract embedded pictures
  5421. void FirstPass(FILE *HelpFile)
  5422. {
  5423.     long FileStart;
  5424.     long TopicNum;
  5425.     int BytesRead;
  5426.     FILEHEADER FileHdr;
  5427.  
  5428.     if(extractmacros)
  5429.     {
  5430.         if(!before31&&SearchFile(HelpFile,"|SYSTEM",&FileStart))
  5431.         {
  5432.             SYSTEMREC SystemRec;
  5433.             long CurrentLocation;
  5434.             char *data;
  5435.  
  5436.             fseek(HelpFile,FileStart,SEEK_SET);
  5437.             myFRead(&FileHdr,sizeof(FileHdr),HelpFile);
  5438.             myFRead(&SysHeader,sizeof(SysHeader),HelpFile);
  5439.             CurrentLocation=12;
  5440.             while(CurrentLocation<FileHdr.FileSize)
  5441.             {
  5442.                 myFRead(&SystemRec,sizeof(SYSTEMREC),HelpFile);
  5443.                 CurrentLocation+=4;
  5444.                 if(SystemRec.DataSize)
  5445.                 {
  5446.                     data=myMalloc(SystemRec.DataSize+1);
  5447.                     myFRead(data,SystemRec.DataSize,HelpFile);
  5448.                     data[SystemRec.DataSize]='\0';
  5449.                     CurrentLocation+=SystemRec.DataSize;
  5450.                     switch(SystemRec.RecordType)
  5451.                     {
  5452.                     case 0x0004:
  5453.                         if(checkexternal)
  5454.                         {
  5455.                             CheckMacro(data);
  5456.                         }
  5457.                         else
  5458.                         {
  5459.                             AddMacro(data);
  5460.                         }
  5461.                         break;
  5462.                     }
  5463.                     free(data);
  5464.                 }
  5465.             }
  5466.         }
  5467.         if(!checkexternal&&!before31&&SearchFile(HelpFile,"|TopicId",&FileStart))
  5468.         {
  5469.             char buffer[1024];
  5470.             int entries,count;
  5471.             long offset;
  5472.             BUFFER buf;
  5473.  
  5474.             entries=GetFirstPage(HelpFile,FileStart,&buf);
  5475.             while(entries)
  5476.             {
  5477.                 for(count=1;count<=entries;count++)
  5478.                 {
  5479.                     myFRead(&offset,sizeof(offset),HelpFile);
  5480.                     StringRead(buffer,sizeof(buffer),HelpFile);
  5481.                     AddTopic(buffer);
  5482.                 }
  5483.                 entries=GetNextPage(HelpFile,&buf);
  5484.             }
  5485.         }
  5486.     }
  5487.     TopicNum=16;
  5488.     TopicUse=TRUE;
  5489.     // extract macros from topic headers
  5490.     browses=0;
  5491.     browsenums=1;
  5492.     if(SearchFile(HelpFile,"|TOPIC",&FileStart))
  5493.     {
  5494.         TOPICLINK TopicLink;
  5495.         TOPICLINK NextTopicLink;
  5496.         char *LinkData1;
  5497.         char *LinkData2;
  5498.         long offset;
  5499.  
  5500.         fseek(HelpFile,FileStart,SEEK_SET);
  5501.         dontCount=FALSE;
  5502.         BytesRead=(int)TopicRead(HelpFile,12L,&TopicLink,sizeof(TopicLink));
  5503.         while(BytesRead==sizeof(TOPICLINK))
  5504.         {
  5505.             offset=TopicOffset;
  5506.             if(TopicLink.DataLen1>sizeof(TOPICLINK))
  5507.             {
  5508.                 LinkData1=myMalloc(TopicLink.DataLen1-sizeof(TOPICLINK)+1);
  5509.                 if(!LinkData1) break;
  5510.                 if(!TopicRead(HelpFile,0L,LinkData1,TopicLink.DataLen1-sizeof(TOPICLINK))) break;
  5511.             }
  5512.             else LinkData1=NULL;
  5513.             if(TopicLink.DataLen1<TopicLink.BlockSize) // read LinkData2 without phrase replacement
  5514.             {
  5515.                 LinkData2=myMalloc(TopicLink.BlockSize-TopicLink.DataLen1+1);
  5516.                 if(!LinkData2) break;
  5517.                 if(!TopicRead(HelpFile,0L,LinkData2,TopicLink.BlockSize-TopicLink.DataLen1)) break;
  5518.             }
  5519.             else LinkData2=NULL;
  5520.             BytesRead=(int)TopicRead(HelpFile,0L,&NextTopicLink,sizeof(NextTopicLink));
  5521.             if(TopicLink.RecordType==TL_TOPICHDR) // display a topic header record
  5522.             {
  5523.                 fprintf(stderr,".");
  5524.                 if(before31)
  5525.                 {
  5526.                     TOPICHEADER30 *TopicHdr;
  5527.  
  5528.                     TopicHdr=(TOPICHEADER30 *)LinkData1;
  5529.                     if(resolvebrowse)
  5530.                     {
  5531.                         if(TopicHdr->NextTopicNum>TopicNum&&TopicHdr->PrevTopicNum>TopicNum
  5532.                         || TopicHdr->NextTopicNum==-1&&TopicHdr->PrevTopicNum>TopicNum
  5533.                         || TopicHdr->NextTopicNum>TopicNum&&TopicHdr->PrevTopicNum==-1)
  5534.                         {
  5535.                             AddBrowse(TopicNum,TopicHdr->NextTopicNum,TopicHdr->PrevTopicNum);
  5536.                         }
  5537.                         else if(TopicHdr->NextTopicNum!=-1&&TopicHdr->NextTopicNum<TopicNum&&TopicHdr->PrevTopicNum!=-1&&TopicHdr->PrevTopicNum<TopicNum)
  5538.                         {
  5539.                             MergeBrowse(TopicNum,TopicHdr->NextTopicNum,TopicHdr->PrevTopicNum);
  5540.                         }
  5541.                         else if(TopicHdr->NextTopicNum!=-1&&TopicHdr->NextTopicNum<TopicNum&&(TopicHdr->PrevTopicNum==-1||TopicHdr->PrevTopicNum>TopicNum))
  5542.                         {
  5543.                             BackLinkBrowse(TopicNum,TopicHdr->NextTopicNum,TopicHdr->PrevTopicNum);
  5544.                         }
  5545.                         else if(TopicHdr->PrevTopicNum!=-1&&TopicHdr->PrevTopicNum<TopicNum&&(TopicHdr->NextTopicNum==-1||TopicHdr->NextTopicNum>TopicNum))
  5546.                         {
  5547.                             LinkBrowse(TopicNum,TopicHdr->NextTopicNum,TopicHdr->PrevTopicNum);
  5548.                         }
  5549.                     }
  5550.                     TopicNum++;
  5551.                 }
  5552.                 else
  5553.                 {
  5554.                     TOPICHEADER *TopicHdr;
  5555.  
  5556.                     TopicHdr=(TOPICHEADER *)LinkData1;
  5557.                     if(resolvebrowse)
  5558.                     {
  5559.                         if(TopicHdr->BrowseFor>offset&&TopicHdr->BrowseBck>offset
  5560.                         || TopicHdr->BrowseFor==-1L&&TopicHdr->BrowseBck>offset
  5561.                         || TopicHdr->BrowseFor>offset&&TopicHdr->BrowseBck==-1L)
  5562.                         {
  5563.                             AddBrowse(offset,TopicHdr->BrowseFor,TopicHdr->BrowseBck);
  5564.                         }
  5565.                         else if(TopicHdr->BrowseFor!=-1L&&TopicHdr->BrowseFor<offset&&TopicHdr->BrowseBck!=-1L&&TopicHdr->BrowseBck<offset)
  5566.                         {
  5567.                             MergeBrowse(offset,TopicHdr->BrowseFor,TopicHdr->BrowseBck);
  5568.                         }
  5569.                         else if(TopicHdr->BrowseFor!=-1L&&TopicHdr->BrowseFor<offset&&(TopicHdr->BrowseBck==-1L||TopicHdr->BrowseBck>offset))
  5570.                         {
  5571.                             BackLinkBrowse(offset,TopicHdr->BrowseFor,TopicHdr->BrowseBck);
  5572.                         }
  5573.                         else if(TopicHdr->BrowseBck!=-1L&&TopicHdr->BrowseBck<offset&&(TopicHdr->BrowseFor==-1L||TopicHdr->BrowseFor>offset))
  5574.                         {
  5575.                             LinkBrowse(offset,TopicHdr->BrowseFor,TopicHdr->BrowseBck);
  5576.                         }
  5577.                     }
  5578.                     if(extractmacros&&TopicLink.DataLen2>0)
  5579.                     {
  5580.                         int i,n;
  5581.                         char *end;
  5582.  
  5583.                         if(TopicLink.DataLen2<=TopicLink.BlockSize-TopicLink.DataLen1)
  5584.                         {
  5585.                             end=LinkData2+TopicLink.DataLen2;
  5586.                         }
  5587.                         else
  5588.                         {
  5589.                             char *q;
  5590.  
  5591.                             q=myMalloc(TopicLink.DataLen2+1);
  5592.                             end=StringPrint(LinkData2,TopicLink.BlockSize-TopicLink.DataLen1,q);
  5593.                             if(end>q+TopicLink.DataLen2)
  5594.                             {
  5595.                                 error("Phrase replacement delivers %u instead of %ld bytes\n",(unsigned int)(end-q),TopicLink.DataLen2);
  5596.                             }
  5597.                             free(LinkData2);
  5598.                             LinkData2=q;
  5599.                         }
  5600.                         *end='\0';
  5601.                         for(i=strlen(LinkData2)+1;i<TopicLink.DataLen2;i+=n+1)
  5602.                         {
  5603.                             n=strlen(LinkData2+i); // because AddMacro destroys string
  5604.                             if(checkexternal)
  5605.                             {
  5606.                                 CheckMacro(LinkData2+i);
  5607.                             }
  5608.                             else
  5609.                             {
  5610.                                 AddMacro(LinkData2+i);
  5611.                             }
  5612.                         }
  5613.                     }
  5614.                 }
  5615.             }
  5616.             else if(TopicLink.RecordType==TL_DISPLAY30||TopicLink.RecordType==TL_DISPLAY||TopicLink.RecordType==TL_TABLE)
  5617.             {
  5618.                 int col,cols;
  5619.                 char filename[20];
  5620.                 unsigned short x1,x2,x3,x4;
  5621.                 short y1;
  5622.                 long l1;
  5623.                 char *ptr;
  5624.                 unsigned short bits;
  5625.  
  5626.                 ptr=scanlong(LinkData1,&l1);
  5627.                 switch(TopicLink.RecordType)
  5628.                 {
  5629.                 case TL_DISPLAY:
  5630.                     ptr=scanword(ptr,&x3);
  5631.                     if(!dontCount) TopicOffset+=x3;
  5632.                     break;
  5633.                 case TL_TABLE:
  5634.                     ptr=scanword(ptr,&x3);
  5635.                     if(!dontCount) TopicOffset+=x3;
  5636.                     cols=(unsigned char)*ptr++;
  5637.                     x4=(unsigned char)*ptr++;
  5638.                     switch(x4)
  5639.                     {
  5640.                     case 0: // found in CALC.HLP and TERMINAL.HLP
  5641.                     case 2:
  5642.                         ptr+=2;
  5643.                     case 1:
  5644.                     case 3:
  5645.                         break;
  5646.                     default:
  5647.                         error("\nunknown column data modifier %02x found\n",x4);
  5648.                     }
  5649.                     ptr+=4*cols;
  5650.                     break;
  5651.                 }
  5652.                 for(col=0;(TopicLink.RecordType==TL_TABLE?*(short *)ptr!=-1:col==0)&&ptr<LinkData1+TopicLink.DataLen1-sizeof(TOPICLINK);col++)
  5653.                 {
  5654.                     if(TopicLink.RecordType==TL_TABLE) ptr+=5;
  5655.                     ptr+=4;
  5656.                     bits=*(unsigned short *)ptr;
  5657.                     ptr+=2;
  5658.                     if(bits&0x0001) ptr=scanlong(ptr,&l1); // found in MSDNCD9.MVB, purpose unknown
  5659.                     if(bits&0x0002) ptr=scanint(ptr,&y1);
  5660.                     if(bits&0x0004) ptr=scanint(ptr,&y1);
  5661.                     if(bits&0x0008) ptr=scanint(ptr,&y1);
  5662.                     if(bits&0x0010) ptr=scanint(ptr,&y1);
  5663.                     if(bits&0x0020) ptr=scanint(ptr,&y1);
  5664.                     if(bits&0x0040) ptr=scanint(ptr,&y1);
  5665.                     if(bits&0x0100) ptr+=3;
  5666.                     if(bits&0x0200)
  5667.                     {
  5668.                         ptr=scanint(ptr,&y1);
  5669.                         while(y1-->0)
  5670.                         {
  5671.                             ptr=scanword(ptr,&x1);
  5672.                             if(x1&0x4000) ptr=scanword(ptr,&x2);
  5673.                         }
  5674.                     }
  5675.                     while(ptr<LinkData1+TopicLink.DataLen1-sizeof(TOPICLINK))
  5676.                     {
  5677.                         if((unsigned char)ptr[0]==0xFF)
  5678.                         {
  5679.                             ptr++;
  5680.                             break;
  5681.                         }
  5682.                         else switch((unsigned char)ptr[0])
  5683.                         {
  5684.                         case 0x21: // dtype (MVB)
  5685.                         case 0x80: // font change
  5686.                             ptr+=3;
  5687.                             break;
  5688.                         case 0x81:
  5689.                         case 0x82:
  5690.                         case 0x83:
  5691.                         case 0x89: // end of hotspot
  5692.                             ptr++;
  5693.                             break;
  5694.                         case 0x86:
  5695.                         case 0x87:
  5696.                         case 0x88:
  5697.                             ptr++;
  5698.                             x1=*ptr++;
  5699.                             ptr=scanlong(ptr,&l1);
  5700.                             switch(x1)
  5701.                             {
  5702.                             case 0x22: // HC31
  5703.                                 ptr=scanword(ptr,&x1);
  5704.                                 // fall thru
  5705.                             case 0x03: // HC30
  5706.                                 switch(((unsigned short *)ptr)[0])
  5707.                                 {
  5708.                                 case 1:
  5709.                                     if(checkexternal)
  5710.                                     {
  5711.                                         CheckBitmap(CreateMap(ptr+2,l1-2));
  5712.                                     }
  5713.                                     else
  5714.                                     {
  5715.                                         for(x2=1;x2<extensions;x2++) if(!extension[x2]) break;
  5716.                                         if(x2>=extensions)
  5717.                                         {
  5718.                                             extension=myReAlloc(extension,(x2+1)*sizeof(char));
  5719.                                             while(extensions<=x2) extension[extensions++]=0;
  5720.                                         }
  5721.                                         sprintf(filename,"bm%u",x2);
  5722.                                         x1=ExtractBitmap(filename,CreateMap(ptr+2,l1-2),l1-2);
  5723.                                         extension[x2]=x1|0x10;
  5724.                                     }
  5725.                                     break;
  5726.                                 }
  5727.                                 break;
  5728.                             case 0x05:
  5729.                                 if(ptr[6]=='!'&&strchr(ptr+7,','))
  5730.                                 {
  5731.                                     if(checkexternal)
  5732.                                     {
  5733.                                         CheckMacro(strchr(ptr+7,',')+1);
  5734.                                     }
  5735.                                     else if(extractmacros)
  5736.                                     {
  5737.                                         AddMacro(strchr(ptr+7,',')+1);
  5738.                                     }
  5739.                                 }
  5740.                                 break;
  5741.                             }
  5742.                             ptr+=l1;
  5743.                             break;
  5744.                         case 0xC8: // macro
  5745.                         case 0xCC: // macro without font change
  5746.                             if(checkexternal)
  5747.                             {
  5748.                                 CheckMacro(ptr+3);
  5749.                             }
  5750.                             else if(extractmacros)
  5751.                             {
  5752.                                 AddMacro(ptr+3);
  5753.                             }
  5754.                             ptr+=*(short *)(ptr+1)+3;
  5755.                             break;
  5756.                         case 0x20: // vfld (MVC)
  5757.                         case 0xE0: // popup jump HC30
  5758.                         case 0xE1: // topic jump HC30
  5759.                         case 0xE2: // popup jump HC31
  5760.                         case 0xE3: // jump jump HC31
  5761.                         case 0xE6: // popup jump without font change
  5762.                         case 0xE7: // jump jump without font change
  5763.                             ptr+=5;
  5764.                             break;
  5765.                         case 0xEA: // popup jump into external file
  5766.                         case 0xEB: // topic jump into external file / secondary window
  5767.                         case 0xEE: // popup jump into external file without font change
  5768.                         case 0xEF: // topic jump into external file / secondary window without font change
  5769.                             if(checkexternal)
  5770.                             {
  5771.                                 switch((unsigned char)ptr[3])
  5772.                                 {
  5773.                                 case 0:
  5774.                                 case 1:
  5775.                                     break;
  5776.                                 case 4:
  5777.                                     if(!CheckHash(ptr+8,*(long *)(ptr+4)))
  5778.                                     {
  5779.                                         fprintf(stderr,"0x%08lx@%s not satisfied\n",*(long *)(ptr+4),ptr+8);
  5780.                                     }
  5781.                                     break;
  5782.                                 case 6:
  5783.                                     if(!CheckHash(ptr+8,*(long *)(ptr+4)))
  5784.                                     {
  5785.                                         fprintf(stderr,"0x%08lx>%s@%s not satisfied\n",*(long *)(ptr+4),ptr+8,strchr(ptr+8,'\0')+1);
  5786.                                     }
  5787.                                     break;
  5788.                                 default:
  5789.                                     error("Unknown modifier %02x in tag %02x\n",(unsigned char)ptr[3],(unsigned char)ptr[0]);
  5790.                                 }
  5791.                             }
  5792.                             ptr+=*(short *)(ptr+1)+3;
  5793.                             break;
  5794.                         case 0x8B: // unknown, found in BACKSDK.MVB
  5795.                         case 0x8C: // unknown, found in BACKSDK.MVB
  5796.                         default:
  5797.                             fprintf(stderr,"\n%02x unknown",(unsigned char)ptr[0]);
  5798.                             ptr++;
  5799.                         }
  5800.                     }
  5801.                 }
  5802.             }
  5803.             if(LinkData1) free(LinkData1);
  5804.             if(LinkData2) free(LinkData2);
  5805.             dontCount=FALSE;
  5806.             memcpy(&TopicLink,&NextTopicLink,sizeof(TopicLink));
  5807.         }
  5808.     }
  5809.     TopicFileStart=0L;
  5810. }
  5811.  
  5812. void DumpTopic(FILE *HelpFile,long FileStart)
  5813. {
  5814.     TOPICLINK TopicLink;
  5815.     TOPICLINK NextTopicLink;
  5816.     char *LinkData1;
  5817.     char *LinkData2;
  5818.     int BytesRead;
  5819.  
  5820.     fseek(HelpFile,FileStart,SEEK_SET);
  5821.     BytesRead=(int)TopicRead(HelpFile,12L,&TopicLink,sizeof(TopicLink));
  5822.     while(BytesRead==sizeof(TOPICLINK))
  5823.     {
  5824.         printf("-------------------------------------------------------------------------\n");
  5825.         printf("TopicLink: BlockSize=%08lx DataLen2=%08lx PrevBlock=%08lx\nNextBlock=%08lx DataLen1=%08lx Type=%02x\n",TopicLink.BlockSize,TopicLink.DataLen2,TopicLink.PrevBlock,TopicLink.NextBlock,TopicLink.DataLen1,TopicLink.RecordType);
  5826.         if(TopicLink.DataLen1>sizeof(TOPICLINK))
  5827.         {
  5828.             LinkData1=myMalloc(TopicLink.DataLen1-sizeof(TOPICLINK));
  5829.             TopicRead(HelpFile,0L,LinkData1,TopicLink.DataLen1-sizeof(TOPICLINK));
  5830.         }
  5831.         else LinkData1=NULL;
  5832.         if(TopicLink.DataLen1<TopicLink.BlockSize) // read LinkData2 without phrase replacement
  5833.         {
  5834.             LinkData2=myMalloc(TopicLink.BlockSize-TopicLink.DataLen1);
  5835.             TopicRead(HelpFile,0L,LinkData2,TopicLink.BlockSize-TopicLink.DataLen1);
  5836.         }
  5837.         else LinkData2=NULL;
  5838.         BytesRead=(int)TopicRead(HelpFile,0L,&NextTopicLink,sizeof(NextTopicLink));
  5839.         if(LinkData1)
  5840.         {
  5841.             HexDumpMemory(LinkData1,TopicLink.DataLen1-sizeof(TOPICLINK));
  5842.         }
  5843.         if(TopicLink.RecordType==TL_TOPICHDR&&before31)
  5844.         {
  5845.             TOPICHEADER30 *TopicHdr;
  5846.  
  5847.             TopicHdr=(TOPICHEADER30 *)LinkData1;
  5848.             printf("BlockSize=%ld PrevTopicNum=%ld NextTopicNum=%ld\n",TopicHdr->BlockSize,TopicHdr->PrevTopicNum,TopicHdr->NextTopicNum);
  5849.         }
  5850.         else if(TopicLink.RecordType==TL_TOPICHDR&&!before31)
  5851.         {
  5852.             TOPICHEADER *TopicHdr;
  5853.  
  5854.             TopicHdr=(TOPICHEADER *)LinkData1;
  5855.             printf("BlockSize=%ld BrowseBck=%08lx BrowseFor=%08lx TopicNum=%ld\n",TopicHdr->BlockSize,TopicHdr->BrowseBck,TopicHdr->BrowseFor,TopicHdr->TopicNum);
  5856.             printf("NonScroll=%08lx Scroll=%08lx NextTopic=%08lx\n",TopicHdr->NonScroll,TopicHdr->Scroll,TopicHdr->NextTopic);
  5857.         }
  5858.         else if(TopicLink.RecordType==TL_DISPLAY30||TopicLink.RecordType==TL_DISPLAY||TopicLink.RecordType==TL_TABLE)
  5859.         {
  5860.             char *ptr;
  5861.             char *str;
  5862.             char *cmd;
  5863.             long l;
  5864.             unsigned short w,bits;
  5865.             unsigned char b,cols,col;
  5866.             short i;
  5867.  
  5868.             ptr=scanlong(LinkData1,&l);
  5869.             printf("expandedsize=%ld ",l);
  5870.             switch(TopicLink.RecordType)
  5871.             {
  5872.             case TL_DISPLAY:
  5873.                 ptr=scanword(ptr,&w);
  5874.                 printf("topicoffsetincrement=%u ",w);
  5875.                 break;
  5876.             case TL_TABLE:
  5877.                 ptr=scanword(ptr,&w);
  5878.                 printf("topicoffsetincrement=%u ",w);
  5879.                 cols=*ptr++;
  5880.                 printf("columns=%d ",cols);
  5881.                 b=*ptr++;
  5882.                 printf("type=%d ",b);
  5883.                 switch(b)
  5884.                 {
  5885.                 case 0:
  5886.                 case 2:
  5887.                     printf("minwidth=%d ",*(short *)ptr);
  5888.                     ptr+=2;
  5889.                 case 1:
  5890.                 case 3:
  5891.                     break;
  5892.                 default:
  5893.                     printf("unknown ");
  5894.                 }
  5895.                 for(i=0;i<cols;i++)
  5896.                 {
  5897.                     printf("width=%d ",((short *)ptr)[0]);
  5898.                     printf("gap=%d ",((short *)ptr)[1]);
  5899.                     ptr+=4;
  5900.                 }
  5901.                 break;
  5902.             }
  5903.             printf("\n");
  5904.             if(TopicLink.DataLen2<=TopicLink.BlockSize-TopicLink.DataLen1)
  5905.             {
  5906.                 str=LinkData2;
  5907.             }
  5908.             else
  5909.             {
  5910.                 str=myMalloc(TopicLink.DataLen2+1);
  5911.                 StringPrint(LinkData2,TopicLink.BlockSize-TopicLink.DataLen1,str);
  5912.                 free(LinkData2);
  5913.                 LinkData2=str;
  5914.             }
  5915.             for(col=0;(TopicLink.RecordType==TL_TABLE?*(short *)ptr!=-1:col==0)&&ptr<LinkData1+TopicLink.DataLen1-sizeof(TOPICLINK);col++)
  5916.             {
  5917.                 if(TopicLink.RecordType==TL_TABLE)
  5918.                 {
  5919.                     printf("column=%d ",*(short *)ptr);
  5920.                     ptr+=2;
  5921.                     printf("%04x ",*(unsigned short *)ptr);
  5922.                     ptr+=2;
  5923.                     printf("%d ",(unsigned char)*ptr++-0x80);
  5924.                 }
  5925.                 printf("%02x ",*ptr++);
  5926.                 printf("%d ",(unsigned char)*ptr++-0x80);
  5927.                 printf("id=%04x ",*(unsigned short *)ptr);
  5928.                 ptr+=2;
  5929.                 bits=*(unsigned short *)ptr;
  5930.                 ptr+=2;
  5931.                 if(bits&0x0001) // found in MSDNCD9.MVB, purpose unknown
  5932.                 {
  5933.                     ptr=scanlong(ptr,&l);
  5934.                     printf("unknownbit01=%ld ",l);
  5935.                 }
  5936.                 if(bits&0x0002)
  5937.                 {
  5938.                     ptr=scanint(ptr,&i);
  5939.                     printf("topspacing=%d ",i);
  5940.                 }
  5941.                 if(bits&0x0004)
  5942.                 {
  5943.                     ptr=scanint(ptr,&i);
  5944.                     printf("bottomspacing=%d ",i);
  5945.                 }
  5946.                 if(bits&0x0008)
  5947.                 {
  5948.                     ptr=scanint(ptr,&i);
  5949.                     printf("linespacing=%d ",i);
  5950.                 }
  5951.                 if(bits&0x0010)
  5952.                 {
  5953.                     ptr=scanint(ptr,&i);
  5954.                     printf("leftindent=%d ",i);
  5955.                 }
  5956.                 if(bits&0x0020)
  5957.                 {
  5958.                     ptr=scanint(ptr,&i);
  5959.                     printf("rightindent=%d ",i);
  5960.                 }
  5961.                 if(bits&0x0040)
  5962.                 {
  5963.                     ptr=scanint(ptr,&i);
  5964.                     printf("firstlineindent=%d ",i);
  5965.                 }
  5966.                 if(bits&0x0080) printf("unknownbit80set ",i);
  5967.                 if(bits&0x0100)
  5968.                 {
  5969.                     b=(unsigned char)*ptr++;
  5970.                     if(b&1) printf("box ");
  5971.                     if(b&2) printf("topborder ");
  5972.                     if(b&4) printf("leftborder ");
  5973.                     if(b&8) printf("bottomborder ");
  5974.                     if(b&0x10) printf("rightborder ");
  5975.                     if(b&0x20) printf("thickborder ");
  5976.                     if(b&0x40) printf("doubleborder ");
  5977.                     if(b&0x80) printf("unknownborder ");
  5978.                     printf("%04x ",*(unsigned short *)ptr);
  5979.                     ptr+=2;
  5980.                 }
  5981.                 if(bits&0x0200)
  5982.                 {
  5983.                     ptr=scanint(ptr,&i);
  5984.                     printf("tabs=%d ",i);
  5985.                     while(i-->0)
  5986.                     {
  5987.                         ptr=scanword(ptr,&w);
  5988.                         printf("stop=%d ",w&0x3FFF);
  5989.                         if(w&0x4000)
  5990.                         {
  5991.                             ptr=scanword(ptr,&w);
  5992.                             if(w==1)
  5993.                             {
  5994.                                 printf("right ");
  5995.                             }
  5996.                             else if(w==2)
  5997.                             {
  5998.                                 printf("center ");
  5999.                             }
  6000.                             else
  6001.                             {
  6002.                                 printf("unknowntabmodifier=%02x ",w);
  6003.                             }
  6004.                         }
  6005.                     }
  6006.                 }
  6007.                 if(bits&0x0400) printf("rightalign ");
  6008.                 if(bits&0x0800) printf("centeralign ");
  6009.                 if(bits&0x1000) printf("keeplinestogether ");
  6010.                 if(bits&0x2000) printf("unknownbit2000set "); // found in PRINTMAN.HLP
  6011.                 if(bits&0x4000) printf("unknownbit4000set "); // found in PRINTMAN.HLP
  6012.                 if(bits&0x8000) printf("unknownbit8000set ");
  6013.                 printf("\n");
  6014.                 while(1)
  6015.                 {
  6016.                     while(*str)
  6017.                     {
  6018.                         if(isprint((unsigned char)*str))
  6019.                         {
  6020.                             putchar(*str++);
  6021.                         }
  6022.                         else
  6023.                         {
  6024.                             printf("(%02x)",*(unsigned char *)str++);
  6025.                         }
  6026.                     }
  6027.                     str++;
  6028.                     if((unsigned char)ptr[0]==0xFF)
  6029.                     {
  6030.                         ptr++;
  6031.                         break;
  6032.                     }
  6033.                     else switch((unsigned char)ptr[0])
  6034.                     {
  6035.                     case 0x20:
  6036.                         printf("{vfld%ld}",*(long *)(ptr+1));
  6037.                         ptr+=5;
  6038.                         break;
  6039.                     case 0x21:
  6040.                         printf("{dtype%d}",*(short *)(ptr+1));
  6041.                         ptr+=3;
  6042.                         break;
  6043.                     case 0x80: // font change
  6044.                         printf("[font=%u]",*(short *)(ptr+1));
  6045.                         ptr+=3;
  6046.                         break;
  6047.                     case 0x81:
  6048.                         printf("[LF]\n");
  6049.                         ptr++;
  6050.                         break;
  6051.                     case 0x82:
  6052.                         printf("[CR]\n");
  6053.                         ptr++;
  6054.                         break;
  6055.                     case 0x83:
  6056.                         printf("[TAB]");
  6057.                         ptr++;
  6058.                         break;
  6059.                     case 0x86:
  6060.                         ptr++;
  6061.                         b=*ptr++;
  6062.                         if(b==0x05) cmd="ewc"; else cmd="bmc";
  6063.                         goto picture;
  6064.                     case 0x87:
  6065.                         ptr++;
  6066.                         b=*ptr++;
  6067.                         if(b==0x05) cmd="ewl"; else cmd="bml";
  6068.                         goto picture;
  6069.                     case 0x88:
  6070.                         ptr++;
  6071.                         b=*ptr++;
  6072.                         if(b==0x05) cmd="ewr"; else cmd="bmr";
  6073.                         goto picture;
  6074.                     picture:
  6075.                         printf("[%s %02x ",cmd,b);
  6076.                         ptr=scanlong(ptr,&l);
  6077.                         switch(b)
  6078.                         {
  6079.                         case 0x22: // HC31
  6080.                             ptr=scanword(ptr,&w);
  6081.                             printf("hotspots=%u ",w);
  6082.                         case 0x03: // HC30
  6083.                             switch(*(unsigned short *)ptr)
  6084.                             {
  6085.                             case 0:
  6086.                                 printf("baggage ");
  6087.                                 break;
  6088.                             case 1:
  6089.                                 printf("embedded ");
  6090.                                 break;
  6091.                             default:
  6092.                                 printf("%04x ",((unsigned short *)ptr)[0]);
  6093.                             }
  6094.                             printf("bm%u]",((unsigned short *)ptr)[1]);
  6095.                             break;
  6096.                         case 0x05:
  6097.                             printf("%04x ",((unsigned short *)ptr)[0]);
  6098.                             printf("%04x ",((unsigned short *)ptr)[1]);
  6099.                             printf("%04x ",((unsigned short *)ptr)[2]);
  6100.                             printf("%s]",ptr+6);
  6101.                             break;
  6102.                         default:
  6103.                             error("Unknown picture flags %02x\n",b);
  6104.                         }
  6105.                         ptr+=l;
  6106.                         break;
  6107.                     case 0x89: // end of hot spot
  6108.                         printf("[U]");
  6109.                         ptr++;
  6110.                         break;
  6111.                     case 0xC8: // macro
  6112.                         printf("[!%s]",ptr+3);
  6113.                         ptr+=*(short *)(ptr+1)+3;
  6114.                         break;
  6115.                     case 0xCC: // macro without font change
  6116.                         printf("[*!%s]",ptr+3);
  6117.                         ptr+=*(short *)(ptr+1)+3;
  6118.                         break;
  6119.                     case 0xE0: // Popup HC30
  6120.                         printf("[^TOPIC%ld]",*(long *)(ptr+1));
  6121.                         ptr+=5;
  6122.                         break;
  6123.                     case 0xE1: // Jump HC30
  6124.                         printf("[TOPIC%ld]",*(long *)(ptr+1));
  6125.                         ptr+=5;
  6126.                         break;
  6127.                     case 0xE2: // Popup HC31
  6128.                         printf("[^%08lx]",*(long *)(ptr+1));
  6129.                         ptr+=5;
  6130.                         break;
  6131.                     case 0xE3: // Jump HC31
  6132.                         printf("[%08lx]",*(long *)(ptr+1));
  6133.                         ptr+=5;
  6134.                         break;
  6135.                     case 0xE6: // Popup without font change
  6136.                         printf("[*^%08lx]",*(long *)(ptr+1));
  6137.                         ptr+=5;
  6138.                         break;
  6139.                     case 0xE7: // Jump without font change
  6140.                         printf("[*%08lx]",*(long *)(ptr+1));
  6141.                         ptr+=5;
  6142.                         break;
  6143.                     case 0xEA: // Popup into external file / secondary window
  6144.                         cmd="^";
  6145.                         goto jump;
  6146.                     case 0xEB: // Jump into external file / secondary window
  6147.                         cmd="";
  6148.                         goto jump;
  6149.                     case 0xEE: // Popup into external file / secondary window without font change
  6150.                         cmd="^*";
  6151.                         goto jump;
  6152.                     case 0xEF: // Jump into external file / secondary window without font change
  6153.                         cmd="*";
  6154.                         goto jump;
  6155.                     jump:
  6156.                         switch(ptr[3])
  6157.                         {
  6158.                         case 0:
  6159.                             printf("[%s%08lx] ",cmd,*(long *)(ptr+4));
  6160.                             break;
  6161.                         case 1: // Popup into secondary window (silly)
  6162.                             printf("[%s%08lx>%d]",cmd,*(long *)(ptr+4),(unsigned char)ptr[8]);
  6163.                             break;
  6164.                         case 4:
  6165.                             printf("[%s%08lx@%s] ",cmd,*(long *)(ptr+4),ptr+8);
  6166.                             break;
  6167.                         case 6: // Popup into external file / secondary window (silly)
  6168.                             printf("[%s%08lx>%s@%s] ",cmd,*(long *)(ptr+4),ptr+8,strchr(ptr+8,'\0')+1);
  6169.                             break;
  6170.                         default:
  6171.                             printf("[");
  6172.                             for(i=0;i<*(short *)(ptr+1);i++) printf("%02x",(unsigned char)ptr[i]);
  6173.                             printf("]");
  6174.                         }
  6175.                         ptr+=*(short *)(ptr+1)+3;
  6176.                         break;
  6177.                     default:
  6178.                         printf("[%02x]",(unsigned char)*ptr++);
  6179.                     }
  6180.                 }
  6181.                 printf("\n");
  6182.             }
  6183.             free(LinkData2);
  6184.             LinkData2=NULL;
  6185.         }
  6186.         if(LinkData2)
  6187.         {
  6188.             if(TopicLink.DataLen2<=TopicLink.BlockSize-TopicLink.DataLen1)
  6189.             {
  6190.                 char *ptr;
  6191.                 char *end;
  6192.  
  6193.                 end=LinkData2+TopicLink.DataLen2;
  6194.                 for(ptr=LinkData2;ptr<end;ptr++)
  6195.                 {
  6196.                     if(isprint((unsigned char)*ptr))
  6197.                     {
  6198.                         putchar(*ptr);
  6199.                     }
  6200.                     else
  6201.                     {
  6202.                         printf("(%02x)",*(unsigned char *)ptr);
  6203.                     }
  6204.                 }
  6205.             }
  6206.             else
  6207.             {
  6208.                 StringPrint(LinkData2,TopicLink.BlockSize-TopicLink.DataLen1,NULL);
  6209.             }
  6210.             printf("\n");
  6211.         }
  6212.         if(LinkData1) free(LinkData1);
  6213.         if(LinkData2) free(LinkData2);
  6214.         memcpy(&TopicLink,&NextTopicLink,sizeof(TopicLink));
  6215.     }
  6216. }
  6217.  
  6218. void HelpDeCompile(FILE *HelpFile,char *dumpfile,int mode)
  6219. {
  6220.     char buffer[81];
  6221.     long FileOffset;
  6222.     FILE *rtf;
  6223.     FILE *hpj;
  6224.  
  6225.     myFRead(&HelpHeader,sizeof(HelpHeader),HelpFile);
  6226.     if(HelpHeader.Magic!=0x5F3F)
  6227.     {
  6228.         fprintf(stderr,"Fatal Error: Not a valid WinHelp file !\n");
  6229.     }
  6230.     else if(!dumpfile)
  6231.     {
  6232.         switch(mode)
  6233.         {
  6234.         case 0:
  6235.             SysLoad(HelpFile);
  6236.             fprintf(stderr,"Decompiling %s...\n",HelpFileTitle);
  6237.             if(before31)
  6238.             {
  6239.                 ToMapLoad(HelpFile);
  6240.             }
  6241.             else
  6242.             {
  6243.                 ContextLoad(HelpFile);
  6244.             }
  6245.             PhraseLoad(HelpFile);
  6246.             ExportBitmaps(HelpFile);
  6247.             fprintf(stderr,"Pass 1...");
  6248.             FirstPass(HelpFile); // valid only after ExportBitmaps
  6249.             printf("\n");
  6250.             _makepath(buffer,NULL,NULL,name,mvp?".MVP":".HPJ");
  6251.             hpj=myFOpen(buffer,"wt");
  6252.             _makepath(buffer,NULL,NULL,name,".ICO");
  6253.             SysList(HelpFile,hpj,buffer);
  6254.             ListBaggage(HelpFile,hpj);
  6255.             AliasList(hpj); // after ContextLoad, before TopicDump
  6256.             _makepath(buffer,NULL,NULL,name,".PH");
  6257.             PhraseList(buffer); // after PhraseLoad
  6258.             _makepath(buffer,NULL,NULL,name,".RTF");
  6259.             rtf=myFOpen(buffer,"wt");
  6260.             fprintf(hpj,"[FILES]\n%s\n\n",buffer);
  6261.             fprintf(rtf,"{\\rtf1\\ansi \\deff0\n");
  6262.             FontLoad(HelpFile,rtf);
  6263.             fprintf(rtf,"{\\stylesheet{\\fs20\\lang1031 \\snext0 Normal;}}{\\info{\\creatim\\yr1995\\mo2\\dy22\\hr2\\min22}{\\revtim\\yr1995\\mo2\\dy26\\hr22\\min24}{\\version9}{\\edmins0}{\\nofpages0}{\\nofwords0}{\\nofchars0}{\\vern16433}}\n"
  6264.             "\\paperw11906\\paperh16838\\margl1417\\margr1417\\margt1417\\margb1134\\gutter0 \\deftab709\\widowctrl\\ftnbj\\hyphhotz425 \\sectd \\linex0\\headery709\\footery709\\colsx709\\endnhere \\pard\\plain \\fs20\\lang1031\n");
  6265.             printf("Pass 2...");
  6266.             TopicDump(HelpFile,rtf,FALSE);
  6267.             fprintf(stderr,"\n");
  6268.             NotInAnyTopic=FALSE;
  6269.             CTXOMAPList(HelpFile,hpj);
  6270.             fprintf(rtf,"}");
  6271.             myFClose(rtf);
  6272.             if(extensions&&strcmp(helpcomp,"HCW")!=0) ListBitmaps(hpj);
  6273.             myFClose(hpj);
  6274.             if(Offsets) printf("Help Compiler will issue Warning 5098: Using old key-phrase table\n");
  6275.             if(missing) printf("Help Compiler will issue Error 1230: File 'missing.bmp' not found\n");
  6276.             if(NotInAnyTopic) printf("Help Compiler will issue Warning 4098: Context string(s) in [MAP] section not defined in any topic\n");
  6277.             if(!extractmacros) printf("Help Compiler may issue Warning 4131: Hash conflict between 'x' and 'y'.\n");
  6278.             if(warnings)
  6279.             {
  6280.                 _makepath(buffer,drive,dir,name,ext);
  6281.                 printf("HELPDECO had problems with %s. Rebuilt helpfile may behave bad.\n",buffer);
  6282.             }
  6283.             if(helpcomp[0])
  6284.             {
  6285.                 _makepath(buffer,NULL,NULL,name,mvp?".MVP":".HPJ");
  6286.                 printf("Use %s %s to recompile helpfile.\n",helpcomp,buffer);
  6287.             }
  6288.             break;
  6289.         case 1:
  6290.             HexDump(HelpFile,HelpHeader.DirectoryStart);
  6291.             break;
  6292.         case 2:
  6293.             ListFiles(HelpFile);
  6294.             break;
  6295.         case 3:
  6296.             SysLoad(HelpFile);
  6297.             ExportBitmaps(HelpFile);
  6298.             PhraseLoad(HelpFile);
  6299.             _makepath(buffer,NULL,NULL,name,".RTF");
  6300.             rtf=myFOpen(buffer,"wt");
  6301.             fprintf(rtf,"{\\rtf1\\ansi \\deff0\n");
  6302.             FontLoad(HelpFile,rtf);
  6303.             fprintf(rtf,"{\\stylesheet{\\fs20\\lang1031 \\snext0 Normal;}}{\\info{\\creatim\\yr1995\\mo2\\dy22\\hr2\\min22}{\\revtim\\yr1995\\mo2\\dy26\\hr22\\min24}{\\version9}{\\edmins0}{\\nofpages0}{\\nofwords0}{\\nofchars0}{\\vern16433}}\n"
  6304.             "\\paperw11906\\paperh16838\\margl1417\\margr1417\\margt1417\\margb1134\\gutter0 \\deftab709\\widowctrl\\ftnbj\\hyphhotz425 \\sectd \\linex0\\headery709\\footery709\\colsx709\\endnhere \\pard\\plain \\fs20\\lang1031\n");
  6305.             TopicDump(HelpFile,rtf,TRUE);
  6306.             fprintf(rtf,"}");
  6307.             myFClose(rtf);
  6308.             break;
  6309.         case 4:
  6310.             SysLoad(HelpFile);
  6311.             _makepath(buffer,NULL,NULL,name,".CNT");
  6312.             rtf=myFOpen(buffer,"wt");
  6313.             GenerateContent(HelpFile,rtf);
  6314.             myFClose(rtf);
  6315.             break;
  6316.         case 5:
  6317.             SysLoad(HelpFile);
  6318.             ListTopic(HelpFile);
  6319.             break;
  6320.         case 6: // check external references
  6321.             SysLoad(HelpFile);
  6322.             fprintf(stderr,"Checking %s...\n",HelpFileTitle);
  6323.             PhraseLoad(HelpFile);
  6324.             ExportBitmaps(HelpFile);
  6325.             FirstPass(HelpFile); // valid only after ExportBitmaps
  6326.             printf("\n");
  6327.             break;
  6328.         }
  6329.     }
  6330.     else
  6331.     {
  6332.         if(!SearchFile(HelpFile,dumpfile,&FileOffset))
  6333.         {
  6334.             buffer[0]='|';
  6335.             strcpy(buffer+1,dumpfile);
  6336.             if(!SearchFile(HelpFile,buffer,&FileOffset))
  6337.             {
  6338.                 fprintf(stderr,"Internal file %s not found.\n",dumpfile);
  6339.                 return;
  6340.             }
  6341.             dumpfile=buffer;
  6342.         }
  6343.         if(mode)
  6344.         {
  6345.             HexDump(HelpFile,FileOffset);
  6346.         }
  6347.         else if(strcmp(dumpfile,"|Phrases")==0||strcmp(dumpfile,"|PhrIndex")==0)
  6348.         {
  6349.             SysLoad(HelpFile);
  6350.             PhraseLoad(HelpFile);
  6351.             PhraseDump();
  6352.         }
  6353.         else if(strcmp(dumpfile,"|SYSTEM")==0)
  6354.         {
  6355.             SysDump(HelpFile,FileOffset);
  6356.         }
  6357.         else if(strcmp(dumpfile,"|FONT")==0)
  6358.         {
  6359.             FontDump(HelpFile,FileOffset);
  6360.         }
  6361.         else if(strcmp(dumpfile,"|TOMAP")==0)
  6362.         {
  6363.             ToMapDump(HelpFile,FileOffset);
  6364.         }
  6365.         else if(strcmp(dumpfile,"|TOPIC")==0)
  6366.         {
  6367.             SysLoad(HelpFile);
  6368.             PhraseLoad(HelpFile);
  6369.             DumpTopic(HelpFile,FileOffset);
  6370.         }
  6371.         else if(strcmp(dumpfile,"|TTLBTREE")==0)
  6372.         {
  6373.             TTLDump(HelpFile,FileOffset);
  6374.         }
  6375.         else if(strcmp(dumpfile,"|Rose")==0)
  6376.         {
  6377.             RoseDump(HelpFile,FileOffset);
  6378.         }
  6379.         else if(strcmp(dumpfile,"|TopicId")==0)
  6380.         {
  6381.             TopicIdDump(HelpFile,FileOffset);
  6382.         }
  6383.         else if(strcmp(dumpfile,"|VIOLA")==0)
  6384.         {
  6385.             VIOLADump(HelpFile,FileOffset);
  6386.         }
  6387.         else if(strcmp(dumpfile,"|Petra")==0)
  6388.         {
  6389.             PetraDump(HelpFile,FileOffset);
  6390.         }
  6391.         else if(dumpfile[0]=='|'&&strcmp(dumpfile+2,"WMAP")==0||strcmp(dumpfile+2,"KWMAP")==0)
  6392.         {
  6393.             KWMapDump(HelpFile,FileOffset);
  6394.         }
  6395.         else if(dumpfile[0]=='|'&&strcmp(dumpfile+2,"WDATA")==0||strcmp(dumpfile+2,"KWDATA")==0)
  6396.         {
  6397.             KWDataDump(HelpFile,FileOffset);
  6398.         }
  6399.         else if(dumpfile[0]=='|'&&strcmp(dumpfile+2,"WBTREE")==0||strcmp(dumpfile+2,"KWBTREE")==0)
  6400.         {
  6401.             KWBTreeDump(HelpFile,FileOffset);
  6402.         }
  6403.         else if(strcmp(dumpfile,"|CATALOG")==0)
  6404.         {
  6405.             CatalogDump(HelpFile,FileOffset);
  6406.         }
  6407.         else if(strcmp(dumpfile,"|CONTEXT")==0)
  6408.         {
  6409.             ContextDump(HelpFile,FileOffset);
  6410.         }
  6411.         else if(strcmp(dumpfile,"|CTXOMAP")==0)
  6412.         {
  6413.             CTXOMAPDump(HelpFile,FileOffset);
  6414.         }
  6415.         else if(strcmp(dumpfile,"|PhrImage")==0)
  6416.         {
  6417.             PhrImageDump(HelpFile,FileOffset);
  6418.         }
  6419.         else
  6420.         {
  6421.         HexDump(HelpFile,FileOffset);
  6422.         }
  6423.     }
  6424. }
  6425.  
  6426. int main(int argc,char *argv[])
  6427. {
  6428.     char buffer[81];
  6429.     FILE *f;
  6430.     int mode;
  6431.     char *filename;
  6432.     char *dumpfile;
  6433.     int i;
  6434.  
  6435.     memset(table,0,sizeof(table));
  6436.     for(i=0;i<9;i++) table['1'+i]=i+1;
  6437.     table['0']=10;
  6438.     table['.']=12;
  6439.     table['_']=13;
  6440.     for(i=0;i<26;i++) table['A'+i]=table['a'+i]=17+i;
  6441.     dumpfile=NULL;
  6442.     filename=NULL;
  6443.     mode=0;
  6444.     for(i=1;i<argc;i++)
  6445.     {
  6446.         if(argv[i][0]=='/'||argv[i][0]=='-') switch(tolower((unsigned char)argv[i][1]))
  6447.         {
  6448.         case 'p':
  6449.             mode=6;
  6450.             extractmacros=TRUE;
  6451.             resolvebrowse=FALSE;
  6452.             checkexternal=TRUE;
  6453.             break;
  6454.         case 'y':
  6455.             overwrite=TRUE;
  6456.             break;
  6457.         case 'c':
  6458.             mode=4;
  6459.             break;
  6460.         case 'x':
  6461.             mode=1;
  6462.             break;
  6463.         case 'd':
  6464.             mode=2;
  6465.             break;
  6466.         case 'r':
  6467.             mode=3;
  6468.             break;
  6469.         case 'w':
  6470.             warn=TRUE;
  6471.             break;
  6472.         case 't':
  6473.             mode=5;
  6474.             break;
  6475.         case 'm':
  6476.             extractmacros=FALSE;
  6477.             break;
  6478.         case 'b':
  6479.             resolvebrowse=FALSE;
  6480.             break;
  6481.         default:
  6482.             fprintf(stderr,"unknown option '%s' ignored\n",argv[i]);
  6483.         }
  6484.         else if(dumpfile)
  6485.         {
  6486.             fprintf(stderr,"additional parameter '%s' ignored\n",argv[i]);
  6487.         }
  6488.         else if(filename)
  6489.         {
  6490.             dumpfile=argv[i];
  6491.         }
  6492.         else
  6493.         {
  6494.             filename=argv[i];
  6495.         }
  6496.     }
  6497.     if(filename)
  6498.     {
  6499.         strupr(filename);
  6500.         _splitpath(filename,drive,dir,name,ext);
  6501.         if(ext[0]=='\0') strcpy(ext,".HLP");
  6502.         mvp=ext[1]=='M';
  6503.         _makepath(buffer,drive,dir,name,ext);
  6504.         f=fopen(buffer,"rb");
  6505.         if(f)
  6506.         {
  6507.             if(((MFILE *)f)->magic==MAGIC)
  6508.             {
  6509.                 fprintf(stderr,"Error opening %s\n",buffer);
  6510.             }
  6511.             else
  6512.             {
  6513.                 HelpDeCompile(f,dumpfile,mode);
  6514.             }
  6515.             myFClose(f);
  6516.         }
  6517.         else
  6518.         {
  6519.             fprintf(stderr,"Can not open '%s'\n",buffer);
  6520.         }
  6521.     }
  6522.     else
  6523.     {
  6524.         fprintf(stderr,"HELPDECO - decompile *.HLP/*.MVB files of Windows 3.0/3.1/3.11/95 - Version 1.8\n"
  6525.                        "M.Winterhoff, Geschw.-Scholl-Ring 17, 38444 Wolfsburg, Germany, CIS 100326,2776\n"
  6526.                        "\n"
  6527.                        "usage:  HELPDECO helpfile[.hlp]           - decompile helpfile into all sources\n"
  6528.                        "        HELPDECO helpfile[.hlp] /r        - decompile into lookalike RTF\n"
  6529.                        "        HELPDECO helpfile[.hlp] /c        - generate a content (*.CNT) file\n"
  6530.                        "        HELPDECO helpfile[.hlp] /p        - check external references\n"
  6531.                        "        HELPDECO helpfile[.hlp] /d        - display internal directory\n"
  6532.                        "        HELPDECO helpfile[.hlp] /x        - HexDump of internal directory\n"
  6533.                        "        HELPDECO helpfile[.hlp] \"internalfile\"     - displays internal file\n"
  6534.                        "        HELPDECO helpfile[.hlp] \"internalfile\" /x  - HexDump of internal file\n"
  6535.                        "option:                         /y        - overwrite without asking\n"
  6536.                        "\n"
  6537.                        "This program is public domain. Use at your own risk. No part of it may be used\n"
  6538.                        "commercially. No fees may be charged on copying. Always distribute with source.\n"
  6539.                        "\n"
  6540.                        "To recreate all source files neccessary to rebuild a Windows helpfile, create\n"
  6541.                        "a directory, change to this directory and call HELPDECO with the path and name\n"
  6542.                        "of the helpfile to dissect. HELPDECO will extract all files contained in the\n"
  6543.                        "helpfile in two passes and deposit them in the current directory. You may then\n"
  6544.                        "rebuild the helpfile using the appropriate help compiler HC30, HC31, HCP, HCW,\n"
  6545.                        "MVC, or WMVC. The file is not binary compatible, but should look and work like\n"
  6546.                        "the original. HELPDECO cannot handle certain large helpfiles.\n");
  6547.     }
  6548.     return 0;
  6549. }
  6550.